/* jsint.c
 * (c) 2002 Mikulas Patocka (Vrxni Ideolog), Petr 'Brain' Kulhavy
 * This file is a part of the Links program, relased under GPL.
 */

/*
 * Ve vsech upcallech plati, ze pokud dostanu ID nejakeho objektu, tak
 * javascript ma k tomu objektu pristupova prava. Jinymi slovy pristupova prava
 * se testuji v upcallech jen, aby se neco neproneslo vratnici ven. Dovnitr se
 * muze donaset vsechno, co si javascript donese, na to ma prava.
 *
 * Navic vsechny upcally dostanou pointer na f_data_c, kde bezi javascript,
 * takze se bude moci testovat, zda javascript nesaha na f_data_c, ke kteremu
 * nema pristupova prava.
 *
 *   Brain
 */

/* Uctovani pameti:
 * js_mem_alloc/js_mem_free se bude pouzivat na struktury fax_me_tender
 * dale se bude pouzivat take ve funkcich pro praci s cookies, protoze string
 * cookies v javascript_context se tez alokuje pomoci js_mem_alloc/js_mem_free.
 */
 
/*
   Retezce:

   vsechny retezce v ramci javascriptu jsou predavany v kodovani f_data->cp
   (tedy tak, jak prisly v dokumentu ze site)
 */


#include "links.h"

tcount jsint_execute_seq = 0;

#ifdef JS

#include "struct.h"
#include "ipret.h"
#include "builtin_keys.h"

/*
vypisuje to: jaky kod byl zarazen do fronty. jaky kod byl predan interpretu do js_execute_code. jaky kod byl vykonan a ukoncen intepretem jsint_done_execution
#define TRACE_EXECUTE
*/

struct js_request
{
	struct js_request* next;
	struct js_request* prev;
	int onclick_submit; /* >=0 (znamena cislo formulare) pokud tohle je request onclick handleru u submit tlacitka nebo onsubmit handleru */
	int onsubmit;   /* dtto pro submit handler */
	struct event ev;  /* event, ktery se ma poslat pri uspechu */
	int write_pos;  /* document.write position from END of document. -1 if document.write cannot be used */
	int wrote;  /* this request called document.write */
	int len;
	tcount seq;
	unsigned char code[1];
};


/* set_cookies bude parsovat takhle:
 * +....=............;...............................+   <- tohle je strinzik
 *      ^            ^
 * najdu 1. rovnase a za nim 1. strednik, najdu nasledujici rovnase a
 * nasledujici strednik, pokud to bude platne (&'='<&';') a 2. jmeno bude
 * "expires", tak to je furt jedna cookie -> poslu Mikulasovi.
 *
 * kdyz ne, tak je to jina susenka a poslu to 1. Mikulasovi
 *
 * pokud najdu ';' a za nim whitespace, za kterym neni rovnase, tak od toho
 * stredniku je to garbaz, kterou vratim do strinziku (fd->js->ctx->cookies)
 */

/* sets all cookies in fd>js->ctx->cookies */
/* final_flush means, that set_cookies was called from jsint_done_execution */






/* JESTLI V TYHLE FUNKCI BUDE NEJAKA BUGA, tak za to muze PerM, Clock a pan GNU,
 * ktery tady kolem rusili, ze jsem se nemohl soustredit. Takze se s
 * pripadnejma reklamacema obratte na ne!
 *
 *  Brain
 */

/* prototypes */
void jsint_send_event(struct f_data_c* fd, struct event* ev);
int jsint_create(struct f_data_c*);
void jsint_done_execution(struct f_data_c*);
int jsint_can_access(struct f_data_c*, struct f_data_c*);
struct f_data_c* jsint_find_recursive(struct f_data_c*, long);   /* line 89 */
void* jsint_find_object(struct f_data_c*, long);
long* add_id(long*, int*, long);
long* add_fd_id(long*, int*, long, long, unsigned char*);
void send_vodevri_v_novym_vokne(struct terminal*, void (*)(struct terminal*, unsigned char*, unsigned char*), struct session*);
void add_all_recursive_in_fd(long**, int*, struct f_data_c*, struct f_data_c*);


void jsint_set_cookies(struct f_data_c* fd, int final_flush)
{
	unsigned char* str;
	unsigned char* next;
	unsigned char* eq1, *semic1, *eq2, *semic2;

	if (!(fd->js) || !(fd->js->ctx))internal("jsint_set_cookies called with NULL context.\n");
	if (!(fd->js->ctx->cookies) || !(fd->rq))return; /* cookies string is empty, nothing to set */
	str = fd->js->ctx->cookies;
	
a_znova:
	eq1 = strchr(str, '=');
	semic1 = strchr(str, ';');
	
	if (!*str || (!final_flush && !semic1)) /* na konci neni strednik a skript jeste bezi, takze to musime vratit do stringu a vypadnout */
	{
		unsigned char* bla = NULL;
		if (*str)bla = stracpy1(str);
		js_mem_free(fd->js->ctx->cookies);
		fd->js->ctx->cookies = bla;
		return;
	}

	/* ted se v str bud vyskytuje strednik, nebo skript uz skoncil */

	if (semic1 && eq1 > semic1) /* '=' je za ';' takze to pred strednikem a strednik skipnem */
	{
		str = semic1 + 1;
		goto a_znova;
	}

	next = semic1 ? semic1 + 1 : str + strlen(str);
	if (!eq1) /* neni tam '=', takze to preskocime */
	{
		str = next;
		goto a_znova;
	}

	/* ted by to mela bejt regulerni susenka */
	
next_par:
	eq2 = NULL, semic2 = NULL;
	if (semic1 != NULL)
	{
		eq2 = strchr(semic1 + 1, '=');
		semic2 = strchr(semic1 + 1, ';');
	}
	
	if (eq2 && semic1 && (final_flush || semic2))
	{
		unsigned char* p = strstr(semic1 + 1, "expires");
		if (!p)p = strstr(semic1 + 1, "Expires");
		if (!p)p = strstr(semic1 + 1, "EXPIRES");
		if (!p)p = strstr(semic1 + 1, "domain");
		if (!p)p = strstr(semic1 + 1, "Domain");
		if (!p)p = strstr(semic1 + 1, "DOMAIN");
		if (!p)p = strstr(semic1 + 1, "path");
		if (!p)p = strstr(semic1 + 1, "Path");
		if (!p)p = strstr(semic1 + 1, "PATH");
		if (!p)p = strstr(semic1 + 1, "comment");
		if (!p)p = strstr(semic1 + 1, "Comment");
		if (!p)p = strstr(semic1 + 1, "COMMENT");
		if (!p)p = strstr(semic1 + 1, "max-age");
		if (!p)p = strstr(semic1 + 1, "Max-age");
		if (!p)p = strstr(semic1 + 1, "Max-Age");
		if (!p)p = strstr(semic1 + 1, "MAX-AGE");
		if (!p)p = strstr(semic1 + 1, "version");
		if (!p)p = strstr(semic1 + 1, "Version");
		if (!p)p = strstr(semic1 + 1, "VERSION");
		if (p && p > semic1 && p < eq2) /* za 1. prirazenim nasleduje "expires=", takze to je porad jedna susenka */
		{
			next = semic2 ? semic2 + 1 : str + strlen(str);
			semic1 = semic2;
			goto next_par;
		}
	}

	if (*next)next[-1] = 0;
	
	for (; *str && WHITECHAR(*str); str++); /* skip whitechars */

	/*debug("set_cookie: \"%s\"", str);*/
	set_cookie(fd->ses->term, fd->rq->url, str);

	str = next;
	goto a_znova;
}


int jsint_object_type(long to_je_on_Padre)
{
	return to_je_on_Padre & JS_OBJ_MASK;
}


int jsint_create(struct f_data_c* fd)
{
	struct js_state* js;

	if (fd->js) internal("javascript state present");
	js = mem_calloc(sizeof(struct js_state));
	if (!(js->ctx = js_create_context(fd, ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT)))
	{
		mem_free(js);
		return 1;
	}
	init_list(js->queue);
	fd->js = js;
	return 0;
}

void jsint_destroy(struct f_data_c* fd)
{
	struct js_state* js = fd->js;
	fd->script_t = 0;
	if (!js) return;
	fd->js = NULL;
	pr(js_destroy_context(js->ctx)) return;
	if (js->src) mem_free(js->src);
	if (js->active) mem_free(js->active);
	js_zaflaknuto_pameti -= js->newdata;
	free_list(js->queue);
	mem_free(js);
}

/* for <a href="javascript:..."> */
void javascript_func(struct session* ses, unsigned char* hlavne_ze_je_vecirek)
{
	unsigned char* code = get_url_data(hlavne_ze_je_vecirek);

	jsint_execute_code(current_frame(ses), code, strlen(code), -1, -1, -1, NULL);
}

void jsint_send_event(struct f_data_c* fd, struct event* ev)
{
	if (!ev || !ev->b) return;
	send_event(fd->ses, ev);
}

/* executes or queues javascript code in frame:
  write_pos is number of bytes from the position where document.write should write to the end of document
  write_pos == -1 if it is not from <SCRIPT> statement and cannot use document.write
*/
/* data je cislo formulare pro onclick submit handler, jinak se nepouziva */
/* ev je udalost, ktera se ma znovu poslat, pokud bylo vraceno true */
void jsint_execute_code(struct f_data_c* fd, unsigned char* code, int len, int write_pos, int onclick_submit, int onsubmit, struct event* ev)
{
	struct js_request* r, *q;

	for (; code && len && *code && ((*code) == ' ' || (*code) == 9 || (*code) == 13 || (*code) == 10); code++, len--);
	
	/*
	FUJ !!!!
	if (!strncasecmp(code,"javascript:",strlen("javascript:")))code+=strlen("javascript:");
	*/

	if (len >= 11 && !casecmp(code, "javascript:", 11)) code += 11, len -= 11;
	
	if (!js_enable)
	{
		jsint_send_event(fd, ev);
		return;
	}

#ifdef TRACE_EXECUTE
	fprintf(stderr, "Submitted: ^^%.*s^^\n", len, code);
#endif

	if (!fd->js && jsint_create(fd))
	{
		jsint_send_event(fd, ev);
		return;
	}
	if ((unsigned)len > MAXINT - sizeof(struct js_request)) overalloc();
	r = mem_calloc(sizeof(struct js_request) + len);
	r->seq = jsint_execute_seq++;
	r->write_pos = write_pos;
	r->len = len;
	r->onclick_submit = onclick_submit;
	r->onsubmit = onsubmit;
	memcpy(r->code, code, len);
	if (ev) memcpy(&r->ev, ev, sizeof(struct event));
	if (write_pos == -1)
	{
		struct list_head* l = (struct list_head*)fd->js->queue.prev;
		add_to_list(*l, r);
	}
	else
	{
		/* add it beyond all <SCRIPT> requests but before non-<SCRIPT> ones */
		foreach(q, fd->js->queue) if (q->write_pos == -1) break;
		q = q->prev;
		add_at_pos(q, r);
	}
	jsint_run_queue(fd);
}

void jsint_done_execution(struct f_data_c* fd)
{
	struct js_request* r, *to_exec;
	struct js_state* js = fd->js;
	struct event ev = { 0, 0, 0, 0 };
	if (!js)
	{
		internal("no js in frame");
		return;
	}
	if (!js->active)
	{
		internal("jsint_done_execution: completion function called on inactive js");
		return;
	}

#ifdef TRACE_EXECUTE
	fprintf(stderr, "Done: ^^%.*s^^\n", js->active->len, js->active->code);
#endif

	/* accept all cookies set by the script */
	jsint_set_cookies(fd, 1);

	/* js->active obsahuje request prave dobehnuteho skriptu */

	/* dobehl onclick_handler a nezaplatil (vratil false), budou se dit veci */
	if (js->active->ev.b && js->ctx->zaplatim)
		memcpy(&ev, &js->active->ev, sizeof(struct event));
	if (js->active->onclick_submit >= 0 && !js->ctx->zaplatim)
	{
		/* pokud je handler od stejneho formulare, jako je defered, tak odlozeny skok znicime a zlikvidujem prislusny onsubmit handler z fronty */
		if (js->active->onclick_submit == fd->ses->defered_data)
		{
			foreach(r, js->queue)
			/* to je onsubmit od naseho formulare, tak ho smazem */
			if (r->onsubmit == js->active->onclick_submit)
			{
				del_from_list(r);
				mem_free(r);
				break;  /* zadny dalsi onsubmit tohoto formulare uz nebude */
			}
			ses_destroy_defered_jump(fd->ses);
		}
	}

	if (js->active->write_pos == -1) mem_free(js->active), js->active = NULL;
	else
	{
		r = js->active;
		js->active = NULL;
		if (r->wrote) js->wrote = 1;
		jsint_scan_script_tags(fd);
		if (!f_is_finished(fd->f_data))
		{
			fd->done = 0;
			fd->parsed_done = 0;
		}
		if (js->wrote && fd->script_t == -1)
		{
			fd->done = 0;
			fd->parsed_done = 0;
			fd_loaded(NULL, fd);
			js->wrote = 0;
		}
		mem_free(r);
	}
	
	to_exec = js->active;
	if (!to_exec && !list_empty(fd->js->queue)) to_exec = fd->js->queue.next;
	if (fd->ses->defered_url && (!to_exec || (to_exec->seq > fd->ses->defered_seq && to_exec->write_pos == -1)))
	{
		unsigned char* url, *target;
		
		url = stracpy(fd->ses->defered_url);
		target = stracpy(fd->ses->defered_url);
		
		goto_url_f(fd->ses, NULL, url, target, fd->ses->defered_target_base, fd->ses->defered_data, 0, 0, 0);
		mem_free(url);
		mem_free(target);
	}
	else
		jsint_run_queue(fd);
	jsint_send_event(fd, &ev);
}

void jsint_run_queue(struct f_data_c* fd)
{
	struct js_request* r;
	struct js_state* js = fd->js;

	if ((!fd->done && fd->f_data) || !js || js->active || list_empty(js->queue)) return;

	r = js->queue.next;
	del_from_list(r);
	js->active = r;
#ifdef TRACE_EXECUTE
	fprintf(stderr, "Executing: ^^%.*s^^\n", r->len, r->code);

#endif
	pr(js_execute_code(js->ctx, r->code, r->len, (void (*)(void*))jsint_done_execution)) {};
}

/* returns: 1 - source is modified by document.write
	  0 - original source
*/

int jsint_get_source(struct f_data_c* fd, unsigned char** start, unsigned char** end)
{
	struct js_state* js = fd->js;

	if (!js || !js->src) return 0;
	if (start) *start = js->src;
	if (end) *end = js->src + js->srclen;
	return 1;
}

/*
 * tests if script running in frame "running" can access document in frame "accessed"
 * 0=no permission, 1=permission OK
 */
int jsint_can_access(struct f_data_c* running, struct f_data_c* accessed)
{
	int a;
	unsigned char* h1, *h2;
	if (!running || !accessed || !running->rq || !accessed->rq) return 0;

	h1 = get_host_name(running->rq->url);
	h2 = get_host_name(accessed->rq->url);
	a = !strcasecmp(h1, h2);
	mem_free(h1);
	mem_free(h2);
	return a;
}


/* doc_id is real document id, whithout any type */
/* fd must be a valid pointer */
struct f_data_c* jsint_find_recursive(struct f_data_c* fd, long doc_id)
{
	struct f_data_c* sub, *fdd;
	if (fd->id == doc_id) return fd;
	foreach(sub, fd->subframes)
	{
		if ((fdd = jsint_find_recursive(sub, doc_id))) return fdd;
	}
	return NULL;
}

/*
 *  This function finds document that has given ID
 */
struct f_data_c* jsint_find_document(long doc_id)
{
	struct f_data_c* fd;
	struct session* ses;
	int type = jsint_object_type(doc_id);
	
	if (type != JS_OBJ_T_DOCUMENT && type != JS_OBJ_T_FRAME)
	{
		unsigned char txt[256];
		snprintf(txt, 256, "jsint_find_document called with type=%d\n", type);
		internal(txt);
	}
	doc_id >>= JS_OBJ_MASK_SIZE;
	foreach(ses, sessions) if ((fd = jsint_find_recursive(ses->screen, doc_id))) return fd;
	return NULL;
}

void jsint_destroy_document_description(struct f_data* f)
{
	struct js_document_description* jsd;
	if (!f)return;
	
	jsd = f->js_doc;
	if (!jsd) return;
	f->js_doc = NULL;
	/* Pro Martina: vsecky polozky vyrobene vyse se tady zase musi uvolnit (jak kurtizana v rimskejch laznich) */
	/* -------------- */
	mem_free(jsd);
}

/* Document has just loaded. Scan for <SCRIPT> tags and execute each of them */

void jsint_scan_script_tags(struct f_data_c* fd)
{
	unsigned char* name, *attr;
	int namelen;
	unsigned char* val, *e, *ee;
	unsigned char* s, *ss, *eof;
	unsigned char* start, *end;
	unsigned char* onload_code = NULL;
	int uv;
	int bs;

	if (!js_enable)return;
	if (!fd->rq || !fd->rq->ce || !fd->f_data) return;
	if (!jsint_get_source(fd, &ss, &eof))
	{
		if (get_file(fd->rq, &ss, &eof)) return;
	}

	d_opt = &fd->f_data->opt;

	s = ss;
se:
while (s < eof && *s != '<') sp:
		s++;
	if (s >= eof || fd->script_t < 0)
	{
		if (onload_code && fd->script_t != -1)
		{
			jsint_execute_code(fd, onload_code, strlen(onload_code), -1, -1, -1, NULL);
		}
		fd->script_t = -1;
		goto ret;
	}
	if (s + 2 <= eof && (s[1] == '!' || s[1] == '?'))
	{
		s = skip_comment(s, eof);
		goto se;
	}
	if (parse_element(s, eof, &name, &namelen, &attr, &s)) goto sp;
	if (!onload_code && namelen == 4 && !casecmp(name, "BODY", 4))
	{
		onload_code = get_attr_val(attr, "onload"); /* if the element doesn't have onload attribute get_attr_val returns NULL */
		goto se;
	}

	if (!onload_code && namelen == 3 && !casecmp(name, "IMG", 3))
	{
		onload_code = get_attr_val(attr, "onload"); /* if the element doesn't have onload attribute get_attr_val returns NULL */
		goto se;
	}

	if (namelen != 6 || casecmp(name, "SCRIPT", 6) || s - ss < fd->script_t) goto se;
	start = end = NULL;
	if ((val = get_attr_val(attr, "src")))
	{
		unsigned char* url;
		if (fd->f_data->script_href_base && ((url = join_urls(fd->f_data->script_href_base, val))))
		{
			int code, version;
			struct additional_file* af = request_additional_file(fd->f_data, url);
			mem_free(url);
			mem_free(val);
			if (!af || !af->rq) goto se;
			if (af->rq->state >= 0) goto ret;
			if (!af->rq->ce) goto se;
			if (!get_http_code(af->rq->ce->head, &code, &version))
			{
				if (code < 200 || code >= 300) goto se;
			}
			if (get_file(af->rq, &start, &end)) goto se;
			if (start == end) goto se;
		}
		else
		{
			mem_free(val);
			goto se;
		}
	}
	e = s;
	uv = 0;
	bs = 0;
	while (e < eof && *e != '<')
	{
	es:
		if (!uv && (*e == '"' || *e == '\'')) uv = *e, bs = 0;
		else if (*e == '\\' && uv) bs = 1;
		else if (*e == uv && !bs) uv = 0;
		else bs = 0;
		e++;
	}
	if (e + 8 <= eof)
	{
		if (/*uv ||*/ casecmp(e, "</SCRIPT", 8)) goto es;
	}
	else e = eof;
	ee = e;
	while (ee < eof && *ee != '>') ee++;
	if (ee < eof) ee++;
	fd->script_t = ee - ss;
	if (!start || !end) jsint_execute_code(fd, s, e - s, eof - ee, -1, -1, NULL);
	else jsint_execute_code(fd, start, end - start, eof - ee, -1, -1, NULL);
ret:
	if (onload_code)mem_free(onload_code);

	d_opt = &dd_opt;
}


struct hopla_mladej
{
	struct form_control* fc;
	struct form_state* fs;
};


/* Returns pointer to the object with given ID in the document, or NULL when
 * there's no such object. Document must be a valid pointer.
 *
 * Pointer type depends on type of object, caller must know the type and
 * interpret the pointer in the right way.
 */

void* jsint_find_object(struct f_data_c* document, long obj_id)
{
	int type = obj_id & JS_OBJ_MASK;
	int orig_obj_id = obj_id;
	obj_id >>= JS_OBJ_MASK_SIZE;

	switch (type)
	{
			/* form element
			 * obj_id can be from 0 up to number of form fields
			 * (document->vs->form_info_len may be actually lower if the fields were never
			 * touched)
			 * returns allocated struct hopla_mladej, you must free it after use
			 */
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
			{
				struct hopla_mladej* hopla;

				struct form_control* fc;
				/*int n=document->vs->form_info_len;*/
				int a = 0;

				if (obj_id < 0/*||obj_id>=n*/)return NULL;
				hopla = mem_alloc(sizeof(struct hopla_mladej));

				if (!(document->f_data))
				{
					mem_free(hopla);
					return NULL;
				};

				foreachback(fc, document->f_data->forms)
				if (fc->g_ctrl_num == obj_id)
				{
					a = 1;
					break;
				}
				if (!a)
				{
					mem_free(hopla);
					return NULL;
				}

				if (!(hopla->fs = find_form_state(document, fc)))
				{
					mem_free(hopla);
					return NULL;
				}
				hopla->fc = fc;
				return hopla;
			}

			/* link
			 * obj_id can be from 0 to (nlinks-1)
			 */
		case JS_OBJ_T_LINK:
			{
				struct link* l;
				int n;

				if (!(document->f_data))return NULL;

				l = document->f_data->links;
				n = document->f_data->nlinks;

				if (obj_id < 0 || obj_id >= n)return NULL;
				return l + obj_id;
			}

			/* form
			 * obj_id is form_num in struct form_control (f_data->forms)
			 */
		case JS_OBJ_T_FORM:
			{
				struct form_control* f;

				if (!(document->f_data))return NULL;
				foreachback(f, document->f_data->forms) if ((f->form_num) == obj_id)return f;
				return NULL;
			}

			/* anchors
			 * obj_id is position in list of all tags
			 */
		case JS_OBJ_T_ANCHOR:
			{
				struct tag* t;
				int a = 0;

				if (!(document->f_data))return NULL;
				foreach(t, document->f_data->tags)
				{
					if (obj_id == a)return t;
					a++;
				}
				return NULL;
			}
			break;

			/* this is document
			 * call jsint_find_document
			 * returned value is struct f_data_c
			 */
		case JS_OBJ_T_FRAME:
		case JS_OBJ_T_DOCUMENT:
			return jsint_find_document(orig_obj_id);

			/* image
			 * returned value is struct g_object_image *
			 */
		case JS_OBJ_T_IMAGE:
#ifdef G
			if (F)
			{
				struct xlist_head* fi;

				if (!document->f_data)return NULL;
				foreach(fi, document->f_data->images)
				{
					struct g_object_image* gi;
					struct g_object_image goi;

					gi = (struct g_object_image*)((char*)fi + ((char*)&goi - (char*) & (goi.image_list)));
					if (gi->id == obj_id)return gi;
				}
				return NULL;
			}
			else
#endif
				return NULL;

		default:
			internal("jsint_find_object: unknown type %d.", type);
			return NULL;  /* never called, but GCC likes it ;-) */
	}
}


long* add_id(long* field, int* len, long id)
{
	long* p;
	int a;
	for (a = 0; a < (*len); a++) /* this object is already on the list */
		if (field[a] == id)return field;

	(*len)++;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	p = mem_realloc(field, (*len) * sizeof(long));

	p[(*len)-1] = id;
	return p;
}

long* add_fd_id(long* field, int* len, long fd, long id, unsigned char* name)
{
	long* p;
	int a;
	for (a = 0; a < (*len); a += 3) /* this object is already on the list */
		if (field[a] == fd && field[a+1] == id)return field;


	(*len) += 3;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	p = mem_realloc(field, (*len) * sizeof(long));

	p[(*len)-3] = fd;
	p[(*len)-2] = id;
	p[(*len)-1] = (name && (*name)) ? (long)stracpy(name) : (long)NULL;
	return p;
}

static long js_upcall_get_frame_id(void* data);

/* finds all objects with name takhle_tomu_u_nas_nadavame
 * in fd and all it's subframes with rq==NULL
 * js_ctx is f_data_c of the accessing script
 */
static long* find_in_subframes(struct f_data_c* js_ctx, struct f_data_c* fd, long* pole_vole, int* n_items, unsigned char* takhle_tomu_u_nas_nadavame)
{
	struct f_data_c* ff;
	struct form_control* f;
#ifdef G
	struct xlist_head* fi;
#endif

	/* search frame */
	foreach(ff, fd->subframes)
	if (ff->loc && ff->loc->name && !strcmp(ff->loc->name, takhle_tomu_u_nas_nadavame) && jsint_can_access(js_ctx, ff)) /* to je on! */
		if (!(pole_vole = add_id(pole_vole, n_items, js_upcall_get_frame_id(ff))))return NULL;

	if (!(fd->f_data))goto a_je_po_ptakach;

#ifdef G
	if (F)
		/* search images */
		foreach(fi, fd->f_data->images)
	{
		struct g_object_image* gi;
		struct g_object_image goi;

		gi = (struct g_object_image*)((char*)fi + ((char*)(&goi) - (char*)(&(goi.image_list))));
		if (gi->name && !strcmp(gi->name, takhle_tomu_u_nas_nadavame))
			if (!(pole_vole = add_id(pole_vole, n_items, JS_OBJ_T_IMAGE + ((gi->id) << JS_OBJ_MASK_SIZE))))return NULL;
	}
#endif
	/* search forms */
	foreachback(f, fd->f_data->forms)
	if (f->form_name && !strcmp(f->form_name, takhle_tomu_u_nas_nadavame)) /* tak tohle JE Jim Beam */
		if (!(pole_vole = add_id(pole_vole, n_items, ((f->form_num) << JS_OBJ_MASK_SIZE) + JS_OBJ_T_FORM)))return NULL;

	/* search form elements */
	foreachback(f, fd->f_data->forms)
	if (f->name && !strcmp(f->name, takhle_tomu_u_nas_nadavame)) /* tak tohle JE Jim Beam */
	{
		long tak_mu_to_ukaz = 0;
		tak_mu_to_ukaz = (f->g_ctrl_num) << JS_OBJ_MASK_SIZE;
		switch (f->type)
		{
			case FC_TEXT:
				tak_mu_to_ukaz |= JS_OBJ_T_TEXT;
				break;
			case FC_PASSWORD:
				tak_mu_to_ukaz |= JS_OBJ_T_PASSWORD;
				break;
			case FC_TEXTAREA:
				tak_mu_to_ukaz |= JS_OBJ_T_TEXTAREA;
				break;
			case FC_CHECKBOX:
				tak_mu_to_ukaz |= JS_OBJ_T_CHECKBOX;
				break;
			case FC_RADIO:
				tak_mu_to_ukaz |= JS_OBJ_T_RADIO;
				break;
			case FC_IMAGE:
			case FC_SELECT:
				tak_mu_to_ukaz |= JS_OBJ_T_SELECT;
				break;
			case FC_SUBMIT:
				tak_mu_to_ukaz |= JS_OBJ_T_SUBMIT ;
				break;
			case FC_RESET:
				tak_mu_to_ukaz |= JS_OBJ_T_RESET ;
				break;
			case FC_HIDDEN:
				tak_mu_to_ukaz |= JS_OBJ_T_HIDDEN ;
				break;
			case FC_BUTTON:
				tak_mu_to_ukaz |= JS_OBJ_T_BUTTON ;
				break;
			default: /* internal("Invalid form element type.\n"); */
				tak_mu_to_ukaz = 0;
				break;
		}
		if (tak_mu_to_ukaz && !(pole_vole = add_id(pole_vole, n_items, tak_mu_to_ukaz)))return NULL;
	}

a_je_po_ptakach:
	/* find in all rq==NULL */
	foreach(ff, fd->subframes)
	if (!(ff->rq)) pole_vole = find_in_subframes(js_ctx, ff, pole_vole, n_items, takhle_tomu_u_nas_nadavame);

	
	return pole_vole;
}

/* resolves name of an object, returns field of all ID's with the name
 * obj_id is object in which we're searching
 * takhle_tomu_u_nas_nadavame is the searched name
 * context is identifier of the javascript context
 * n_items is number of returned items
 *
 * on error returns NULL
 */
long* jsint_resolve(void* context, long obj_id, char* takhle_tomu_u_nas_nadavame, int* n_items)
{
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)context;
	long* pole_vole;
	*n_items = 0;

	if (!takhle_tomu_u_nas_nadavame || !(*takhle_tomu_u_nas_nadavame))return NULL;
	pole_vole = mem_alloc(sizeof(long));
	switch (jsint_object_type(obj_id))
	{
			/* searched object can be a frame, image, form or a form element */
		case JS_OBJ_T_DOCUMENT:
		case JS_OBJ_T_FRAME:
			fd = jsint_find_document(obj_id);
			if (!fd || !(jsint_can_access(js_ctx, fd)))break;

			pole_vole = find_in_subframes(js_ctx, fd, pole_vole, n_items, takhle_tomu_u_nas_nadavame);
			break;

			/* searched name can be a form element */
		case JS_OBJ_T_FORM:
			{
				struct form_control* fc = jsint_find_object(js_ctx, obj_id);
				struct form_control* f;
				if (!fc)
				{
					mem_free(pole_vole);
					return NULL;
				}

				if (!(js_ctx->f_data))
				{
					mem_free(pole_vole);
					return NULL;
				}
				foreachback(f, js_ctx->f_data->forms)
				{
					if (f->form_num == fc->form_num) /* this form */
						if (f->name && !strcmp(f->name, takhle_tomu_u_nas_nadavame)) /* this IS Jim Beam */
						{
							long tak_mu_to_ukaz = 0;
							tak_mu_to_ukaz = (f->g_ctrl_num) << JS_OBJ_MASK_SIZE;
							switch (f->type)
							{
								case FC_TEXT:
									tak_mu_to_ukaz |= JS_OBJ_T_TEXT;
									break;
								case FC_PASSWORD:
									tak_mu_to_ukaz |= JS_OBJ_T_PASSWORD;
									break;
								case FC_TEXTAREA:
									tak_mu_to_ukaz |= JS_OBJ_T_TEXTAREA;
									break;
								case FC_CHECKBOX:
									tak_mu_to_ukaz |= JS_OBJ_T_CHECKBOX;
									break;
								case FC_RADIO:
									tak_mu_to_ukaz |= JS_OBJ_T_RADIO;
									break;
								case FC_IMAGE:
								case FC_SELECT:
									tak_mu_to_ukaz |= JS_OBJ_T_SELECT;
									break;
								case FC_SUBMIT:
									tak_mu_to_ukaz |= JS_OBJ_T_SUBMIT ;
									break;
								case FC_RESET:
									tak_mu_to_ukaz |= JS_OBJ_T_RESET ;
									break;
								case FC_HIDDEN:
									tak_mu_to_ukaz |= JS_OBJ_T_HIDDEN ;
									break;
								case FC_BUTTON:
									tak_mu_to_ukaz |= JS_OBJ_T_BUTTON ;
									break;
								default:
									tak_mu_to_ukaz = 0;
									break;
									/* internal("Invalid form element type.\n"); */
							}
							if ((tak_mu_to_ukaz & JS_OBJ_MASK) && !(pole_vole = add_id(pole_vole, n_items, tak_mu_to_ukaz)))return NULL;
						}
				}
			}
			break;
	}
	if (!pole_vole)return NULL;
	if (!(*n_items))
	{
		mem_free(pole_vole);
		pole_vole = NULL;
	}
	return pole_vole;
}

/*------------------------>>>>>>>> UPCALLS <<<<<<<<-------------------------*/


/* tyhle upcally se volaji ze select smycky:

  void js_upcall_confirm(void *data)
  void js_upcall_alert(void * data)
  void js_upcall_close_window(void *data)
  void js_upcall_get_string(void *data)
  void js_upcall_goto_url(void * data)
  void js_upcall_goto_history(void * data)
  void js_upcall_set_image_src(void* data)

V nich se musi volat js_spec_vykill_timer, aby se znicil timer, ktery upcall
zavolal.

Tyto upcally MUZOU dostavat f_data_c pointer primo, protoze kdyz ten f_data_c
umre a s nim i ten JS, tak se timery znicej --- tudiz se nic nestane.
*/


static void redraw_document(struct f_data_c* f)
{
	/*
	if (F) {
	  f->xl = -1;
	  f->yl = -1;
	  draw_to_window(f->ses->win, (void (*)(struct terminal *, void *))draw_doc, f);
	}
	*/
	draw_fd(f);
}

struct js_document_description* js_upcall_get_document_description(void* p, long doc_id)
{
	struct js_document_description* js_doc;
	struct f_data* f;
	struct f_data_c* pfd = p;
	struct f_data_c* fd;
	fd = jsint_find_document(doc_id);
	if (!fd || !fd->f_data || !jsint_can_access(pfd, fd)) return NULL;
	f = fd->f_data;
	if (f->js_doc) return f->js_doc;
	js_doc = mem_calloc(sizeof(struct js_document_description));
	/* Pro Martina: pridat sem prohlizeni f_data a vytvoreni struktury */
	/* -------------- */
	return f->js_doc = js_doc;
}


/* returns ID of a document with the javascript */
long js_upcall_get_document_id(void* data)
{
	struct f_data_c* fd;
	if (!data)internal("js_upcall_get_document_id called with NULL pointer!");

	fd = (struct f_data_c*)data;
	return (((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT);
}


/* same as get_document_id, but returned type is FRAME */
static long js_upcall_get_frame_id(void* data)
{
	struct f_data_c* fd;
	if (!data)internal("js_upcall_get_document_id called with NULL pointer!");

	fd = (struct f_data_c*)data;
	return (((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_FRAME);
}


/* writes "len" bytes starting at "str" to document */
void js_upcall_document_write(void* p, unsigned char* str, int len)
{
	int pos;
	unsigned char* s;
	struct f_data_c* fd = p;
	struct js_state* js = fd->js;
	if (!js)return;
	if (!js->active) internal("js_upcall_document_write: no request active");
	if (js->active->write_pos == -1) return;
	if (js->active->write_pos < 0) internal("js_upcall_document_write: js->active trashed");
	if (!js->src)
	{
		unsigned char* s, *eof;
		if (get_file(fd->rq, &s, &eof)) return;
		if (!(js->src = memacpy(s, eof - s))) return;
		js->srclen = eof - s;
	}
	if ((unsigned)js->srclen + (unsigned)len > MAXINT) overalloc();
	if ((unsigned)js->srclen + (unsigned)len < (unsigned)len) overalloc();
	s = mem_realloc(js->src, js->srclen + len);
	js->src = s;
	if ((pos = js->srclen - js->active->write_pos) < 0) pos = 0;
	memmove(s + pos + len, s + pos, js->srclen - pos);
	memcpy(s + pos, str, len);
	js->srclen += len;
	js->newdata += len;
	js_zaflaknuto_pameti += len;
	js->active->wrote = 1;
}


/* returns title of actual document (=document in the script context) */
/* when an error occurs, returns NULL */
/* returned string should be deallocated after use */
unsigned char* js_upcall_get_title(void* data)
{
	struct f_data_c* fd;
	unsigned char* title, *t;
	struct conv_table* ct;

	if (!data)internal("js_upcall_get_title called with NULL pointer!");
	fd = (struct f_data_c*)data;

	title = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_title(fd->ses, title, MAX_STR_LEN)))
	{
		mem_free(title);
		return NULL;
	}
	if (fd->f_data)
	{
		ct = get_translation_table(fd->f_data->opt.cp, fd->f_data->cp);
		t = convert_string(ct, title, strlen(title), NULL);
		mem_free(title);
		title = t;
	}
	return title;
}


/* sets title of actual document (=document in the script context) */
/* string title will be deallocated after use */
void js_upcall_set_title(void* data, unsigned char* title)
{
	unsigned char* t;
	struct conv_table* ct;
	struct f_data_c* fd;
	int l = 0;

	if (!data)internal("js_upcall_get_title called with NULL pointer!");
	fd = (struct f_data_c*)data;

	if (!title)return;
	
	if (!(fd->f_data))
	{
		mem_free(title);
		return;
	}
	if (fd->f_data->title)mem_free(fd->f_data->title);
	fd->f_data->title = init_str();
	fd->f_data->uncacheable = 1;
	ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
	t = convert_string(ct, title, strlen(title), NULL);
	add_to_str(&(fd->f_data->title), &l, t);
	mem_free(t);

	mem_free(title);
	redraw_document(fd);
}


/* returns URL of actual document (=document in the script context) */
/* when an error occurs, returns NULL */
/* returned string should be deallocated after use */
unsigned char* js_upcall_get_location(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}
	return loc;
}


/* returns string containing last modification date */
/* or NULL when the date is not known or when an error occurs */
unsigned char* js_upcall_document_last_modified(void* data, long document_id)
{
	struct f_data_c* fd;
	struct f_data_c* document;
	unsigned char* retval;
	
	document = jsint_find_document(document_id);
	if (!data)internal("js_upcall_document_last_modified called with NULL pointer!");
	fd = (struct f_data_c*)data;

	if (!document)return NULL;  /* document not found */
	if (!jsint_can_access(fd, document))return NULL; /* you have no permissions to look at the document */
	
	if (!fd->rq || !fd->rq->ce)return NULL;
	retval = stracpy(fd->rq->ce->last_modified);

	return retval;
}


/* returns allocated string with user-agent */
unsigned char* js_upcall_get_useragent(void* data)
{
	struct f_data_c* fd;
	unsigned char* retval = init_str();
	int l = 0;
	
	if (!data)internal("js_upcall_get_useragent called with NULL pointer!");
	fd = (struct f_data_c*)data;

	if (!http_options.header.fake_useragent || !(*http_options.header.fake_useragent))
	{
		add_to_str(&retval, &l, "Links (" VERSION_STRING "; ");
		add_to_str(&retval, &l, system_name);
		add_to_str(&retval, &l, ")");
	}
	else
	{
		add_to_str(&retval, &l, http_options.header.fake_useragent);
	}

	return retval;
}


/* returns allocated string with browser name */
unsigned char* js_upcall_get_appname(void)
{
	if (!http_options.header.fake_useragent || !(*http_options.header.fake_useragent))
		return stracpy("Links");
	else
		return stracpy(http_options.header.fake_useragent);
}


/* returns allocated string with browser name */
unsigned char* js_upcall_get_appcodename(void)
{
	if (!http_options.header.fake_useragent || !(*http_options.header.fake_useragent))
		return stracpy("Links");
	else
		return stracpy(http_options.header.fake_useragent);
}


/* returns allocated string with browser version: "version_number (system_name)" */
unsigned char* js_upcall_get_appversion(void)
{
	unsigned char* str;
	int l = 0;

	if (http_options.header.fake_useragent && (*http_options.header.fake_useragent))return stracpy(http_options.header.fake_useragent);
	str = init_str();
	add_to_str(&str, &l, VERSION_STRING);
	add_to_str(&str, &l, " (");
	add_to_str(&str, &l, system_name);
	add_to_str(&str, &l, ")");
	return str;
}


/* returns allocated string with referrer */
unsigned char* js_upcall_get_referrer(void* data)
{
	struct f_data_c* fd;
	unsigned char* retval = init_str();
	unsigned char* loc;
	int l = 0;
	
	if (!data)internal("js_upcall_get_referrer called with NULL pointer!");
	fd = (struct f_data_c*)data;

	switch (http_options.header.referer)
	{
		case REFERER_FAKE:
			add_to_str(&retval, &l, http_options.header.fake_referer);
			break;

		case REFERER_SAME_URL:
			loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
			if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
			{
				mem_free(loc);
				break;
			}
			add_to_str(&retval, &l, loc);
			mem_free(loc);
			break;

		case REFERER_REAL:
			{
				unsigned char* post;

				if (!fd->rq || !(fd->rq->prev_url))break; /* no referrer */
				post = strchr(fd->rq->prev_url, POST_CHAR);
				if (!post)add_to_str(&retval, &l, fd->rq->prev_url);
				else add_bytes_to_str(&retval, &l, fd->rq->prev_url, post - fd->rq->prev_url);
			}
			break;
	}

	return retval;
}

struct gimme_js_id
{
	long id; /* id of f_data_c */
	long js_id; /* unique id of javascript */
};

/* tady se netestuje js_id, protoze BFU to chce killnout, tak to proste killne */
/* aux function for all dialog upcalls */
static void js_kill_script_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js))return;
	js_downcall_game_over(fd->js->ctx);   /* call downcall */
}



/* aux function for js_upcall_confirm */
static void js_upcall_confirm_ok_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_true(fd->js->ctx);   /* call downcall */
}


/* aux function for js_upcall_confirm */
static void js_upcall_confirm_cancel_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_false(fd->js->ctx);   /* call downcall */
}


/* creates dialog with text s->string and buttons OK/Cancel */
/* s->string will be dealocated */
/* s will be dealocated too */
/* must be called from select loop */
void js_upcall_confirm(void* data)
{
	struct fax_me_tender_string* s = (struct fax_me_tender_string*)data;
	struct gimme_js_id* jsid;
	struct f_data_c* fd;
	struct terminal* term;
	unsigned char* txt;

	if (!s)internal("js_upcall_confirm called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd = (struct f_data_c*)(s->ident);
	term = fd->ses->term;

	if (!fd->js)return;
	jsid = mem_alloc(sizeof(struct gimme_js_id));
	
	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	/* fill in jsid */
	jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
	jsid->js_id = fd->js->ctx->js_id;
	
	skip_nonprintable(s->string);
	if (fd->f_data)
	{
		struct conv_table* ct;
		
		ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
		txt = convert_string(ct, s->string, strlen(s->string), NULL);
	}
	else
		txt = stracpy(s->string);
	js_mem_free(s->string);
	msg_box(
	  term,   /* terminal */
	  getml(txt, jsid, NULL), /* memory blocks to free */
	  TEXT(T_QUESTION),   /* title */
	  AL_CENTER,   /* alignment */
	  txt,   /* message */
	  jsid,   /* data for button functions */
	  3,   /* # of buttons */
	  TEXT(T_OK), js_upcall_confirm_ok_pressed, B_ENTER, /* first button */
	  TEXT(T_CANCEL), js_upcall_confirm_cancel_pressed, B_ESC, /* second button */
	  TEXT(T_KILL_SCRIPT), js_kill_script_pressed, NULL
	);

	js_mem_free(s);
}


/* aux function for js_upcall_alert */
static void js_upcall_alert_ok_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* gets struct fax_me_tender_string* */
/* creates dialog with title "Alert" and message got from struct fax_me_tender_string */
/* structure and the text are both deallocated */
/* must be called from select loop */
void js_upcall_alert(void* data)
{
	struct fax_me_tender_string* s = (struct fax_me_tender_string*)data;
	struct gimme_js_id* jsid;
	struct f_data_c* fd;
	struct terminal* term;
	unsigned char* txt;

	if (!s)internal("Alert called with NULL pointer.\n"); /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd = (struct f_data_c*)(s->ident);
	term = fd->ses->term;

	if (!fd->js) return;
	jsid = mem_alloc(sizeof(struct gimme_js_id));
	
	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	/* fill in jsid */
	jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
	jsid->js_id = fd->js->ctx->js_id;
	
	skip_nonprintable(s->string);
	if (fd->f_data)
	{
		struct conv_table* ct;
		
		ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
		txt = convert_string(ct, s->string, strlen(s->string), NULL);
	}
	else
		txt = stracpy(s->string);
	js_mem_free(s->string);
	msg_box(
	  term,   /* terminal */
	  getml(txt, jsid, NULL), /* memory blocks to free */
	  TEXT(T_ALERT),   /* title */
	  AL_CENTER,   /* alignment */
	  txt,   /* message */
	  jsid,   /* data for button functions */
	  2,   /* # of buttons */
	  TEXT(T_OK), js_upcall_alert_ok_pressed, B_ENTER | B_ESC,
	  TEXT(T_KILL_SCRIPT), js_kill_script_pressed, NULL
	);

	js_mem_free(s);
}


/* aux function for js_upcall_close_window */
/* tady se netestuje js_id, protoze BFU zmacklo, ze chce zavrit okno a v
 * nekterych pripadech by ho to nezavrelo (kdyby se testovalo) a to by vypadalo
 * blbe */
static void js_upcall_close_window_yes_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	really_exit_prog(fd->ses);
}


/* asks user if he really wants to close the window and calls really_exit_prog */
/* argument is struct fax_me_tender_nothing* */
/* must be called from select loop */
void js_upcall_close_window(void* data)
{
	struct fax_me_tender_nothing* s = (struct fax_me_tender_nothing*)data;
	struct f_data_c* fd;
	struct terminal* term;

	if (!s)internal("js_upcall_close_window called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd = (struct f_data_c*)(s->ident);
	if (!fd->js) return;
	term = fd->ses->term;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	if (js_manual_confirmation)
	{
		struct gimme_js_id* jsid;

		jsid = mem_alloc(sizeof(struct gimme_js_id));

		/* fill in jsid */
		jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
		jsid->js_id = fd->js->ctx->js_id;
		
		msg_box(
		  term,   /* terminal */
		  getml(jsid, NULL),  /* memory blocks to free */
		  TEXT(T_EXIT_LINKS),   /* title */
		  AL_CENTER,   /* alignment */
		  TEXT(T_SCRIPT_TRYING_TO_CLOSE_WINDOW),   /* message */
		  jsid,   /* data for button functions */
		  2,   /* # of buttons */
		  TEXT(T_YES), js_upcall_close_window_yes_pressed, NULL,
		  TEXT(T_KILL_SCRIPT), js_kill_script_pressed, NULL
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		if (term->next == term->prev && are_there_downloads())
			query_exit(fd->ses);
		else
			really_exit_prog(fd->ses);
	}
}


/* returns parent window ID of the script */
long js_upcall_get_window_id(void* data)
{
	struct f_data_c* fd;
	if (!data)internal("js_upcall_get_window_id called with NULL pointer!");

	fd = (struct f_data_c*)data;
	return ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_FRAME;
}



/* aux function for js_upcall_get_string */
static void js_upcall_get_string_ok_pressed(void* data, unsigned char* str)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_string(fd->js->ctx, stracpy(str));   /* call downcall */
}


struct history js_get_string_history = {0, {&js_get_string_history.items, &js_get_string_history.items}};


/* creates input field for string, with text s->string1, default response
 * s->string2 and buttons OK/Kill Script
 * s->string1 and s->string2 will be dealocated
 * s will be dealocated too
 * must be called from select loop */

void js_upcall_get_string(void* data)
{
	struct fax_me_tender_2_stringy* s = (struct fax_me_tender_2_stringy*)data;
	struct gimme_js_id* jsid;
	struct f_data_c* fd;
	struct terminal* term;
	unsigned char* str1, *str2;

	if (!s)internal("js_upcall_get_string called with NULL pointer\n");  /* to jenom kdyby na mne PerM zkousel naky oplzlosti... */

	/* context must be a valid pointer ! */
	fd = (struct f_data_c*)(s->ident);
	term = fd->ses->term;

	if (!fd->js) return;
	jsid = mem_alloc(sizeof(struct gimme_js_id));

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	/* fill in jsid */
	jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
	jsid->js_id = fd->js->ctx->js_id;

	str1 = stracpy(s->string1);
	str2 = stracpy(s->string2);
	js_mem_free(s->string1);
	js_mem_free(s->string2);

	input_field(
	  term,   /* terminal */
	  getml(str1, str2, jsid, NULL), /* mem to free */
	  TEXT(T_ENTER_STRING),  /* title */
	  str1,   /* question */
	  jsid,   /* data for functions */
	  &js_get_string_history,   /* history */
	  MAX_INPUT_URL_LEN,   /* string len */
	  str2,  /* string to fill the dialog with */
	  0,  /* min value */
	  0,  /* max value */
	  NULL,  /* check fn */
	  TEXT(T_OK),   /* ok button */
	  js_upcall_get_string_ok_pressed,
	  TEXT(T_KILL_SCRIPT),  /* cancel button */
	  js_kill_script_pressed,
	  NULL
	);
	js_mem_free(s);
}


/* clears window with javascript */
/* must be called from select loop */
/* javascript must halt before calling this upcall */
void js_upcall_clear_window(void* data)
{
	/* context must be a valid pointer ! */
	/*struct f_data_c *fd=(struct f_data_c*)data;*/
	/* no jsint_destroy context or so here, it's called automatically from reinit_f_data_c */
	/*
	zatim jsem to zrusil ... tahle funkce musi byt volana pres timer, takhle je to uplne blbe a spadne to pri kazdem volani -- Mikulas

	to je <|>vina, v komentari je jasne napsano, ze tahle fce musi byt volana ze select loop, takze to nema co padat -- Brain

	no prave!! ze select smycky == z timeru. Z javascriptu to volat nemuzes, protoze to pod sebou ten kontext javascriptu smaze, a ten interpret spadne, protoze jeho kontext uz nebude existovat. -- Mikulas

	reinit_f_data_c(fd);
	*/
}


/* returns allocated string with window name */
unsigned char* js_upcall_get_window_name(void* data)
{
	/* context must be a valid pointer ! */
	struct f_data_c* fd = (struct f_data_c*)data;

	return fd->loc ? stracpy(fd->loc->name) : NULL;
}


/* returns allocated field of ID's of links in JS document
 * number of links is stored in len
 * if number of links is 0, returns NULL
 * on error returns NULL too
 */
long* js_upcall_get_links(void* data, long document_id, int* len)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct link* l;
	int a;
	long* to_je_Ono;

	fd = jsint_find_document(document_id);
	if (!js_ctx)internal("js_upcall_get_links called with NULL context pointer\n");
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;
	if (!(fd->f_data))return NULL;
	*len = fd->f_data->nlinks;
	if (!(*len))return NULL;
	l = fd->f_data->links;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	to_je_Ono = mem_alloc((*len) * sizeof(long));
	
	for (a = 0; a < (*len); a++)
		/*to_je_Ono[a]=JS_OBJ_T_LINK+(((l+a)->num)<<JS_OBJ_MASK_SIZE);*/
		to_je_Ono[a] = JS_OBJ_T_LINK + (a << JS_OBJ_MASK_SIZE);

	return to_je_Ono;
}


/* returns allocated string with TARGET of the link
 * if the link doesn't exist in the document, returns NULL
 */
unsigned char* js_upcall_get_link_target(void* data, long document_id, long link_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct link* l;

	if (!js_ctx)internal("js_upcall_get_link_target called with NULL context pointer\n");
	if ((link_id & JS_OBJ_MASK) != JS_OBJ_T_LINK)return NULL; /* this isn't link */

	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	l = jsint_find_object(fd, link_id);
	if (!l)return NULL;

	return stracpy((l->target) ? (l->target) : (unsigned char*)(""));
}


/* returns allocated field of ID's of forms in JS document
 * number of forms is stored in len
 * if number of forms is 0, returns NULL
 * on error returns NULL too
 */
long* js_upcall_get_forms(void* data, long document_id, int* len)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc;
	long* to_je_Ono;
	long last = 0;

	if (!js_ctx)internal("js_upcall_get_forms called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if (!(fd->f_data))return NULL;

	to_je_Ono = mem_alloc(sizeof(long));

	*len = 0;

	foreachback(fc, fd->f_data->forms)
	{
		long* p;
		int a;

		if ((*len) && (fc->form_num) == last)continue;
		for (a = 0; a < (*len); a++)
			if ((to_je_Ono[a] >> JS_OBJ_MASK_SIZE) == (fc->form_num))goto already_have; /* we already have this number */

		(*len)++;
		if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
		p = mem_realloc(to_je_Ono, (*len) * sizeof(long));
		to_je_Ono = p;
		to_je_Ono[(*len)-1] = JS_OBJ_T_FORM | ((fc->form_num) << JS_OBJ_MASK_SIZE);
		last = fc->form_num;
	already_have:
		;
	}

	if (!(*len))
	{
		mem_free(to_je_Ono);
		to_je_Ono = NULL;
	}

	return to_je_Ono;
}


/* returns allocated string with the form action
 * when an error occurs, returns NULL
 */
unsigned char* js_upcall_get_form_action(void* data, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc;

	if (!js_ctx)internal("js_upcall_get_form_action called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return NULL; /* this isn't form */

	fc = jsint_find_object(fd, form_id);
	if (!fc)return NULL;

	return stracpy(fc->action);
}



/* sets form action
 */
void js_upcall_set_form_action(void* context, long document_id, long form_id, unsigned char* action)
{
	struct f_data_c* js_ctx = (struct f_data_c*)context;
	struct f_data_c* fd;
	struct form_control* fc;

	if (!js_ctx)
	{
		internal("js_upcall_set_form_action called with NULL context pointer\n");
	}

	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))
	{
		if (action) mem_free(action);
		return;
	}

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)
	{
		if (action) mem_free(action);
		return;
	}

	fc = jsint_find_object(fd, form_id);
	if (!fc)
	{
		if (action) mem_free(action);
		return;
	}

	if (fc->action)
	{
		mem_free(fc->action);
		if (fd->loc && fd->loc->url)
			fc->action = join_urls(fd->loc->url, action);
		else
			fc->action = stracpy(action);
		fd->f_data->uncacheable = 1;
	}

	if (action)
	{
		mem_free(action);
	}
}


/* returns allocated string with the form target
 * when an error occurs, returns NULL
 */
unsigned char* js_upcall_get_form_target(void* data, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc;

	if (!js_ctx)internal("js_upcall_get_form_target called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return NULL; /* this isn't form */

	fc = jsint_find_object(fd, form_id);
	if (!fc)return NULL;
	
	return stracpy(fc->target);
}



/* returns allocated string with the form method
 * when an error occurs, returns NULL
 */
unsigned char* js_upcall_get_form_method(void* data, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc;

	if (!js_ctx)internal("js_upcall_get_form_method called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return NULL; /* this isn't form */

	fc = jsint_find_object(fd, form_id);
	if (!fc)return NULL;
	
	switch (fc->method)
	{
		case FM_GET:
			return stracpy("GET");

		case FM_POST:
		case FM_POST_MP:
			return stracpy("POST");

		default:
			internal("Invalid form method!\n");
			return NULL;  /* never called, but GCC likes it */
	}
}



/* returns allocated string with the form encoding (value of attribute enctype)
 * when an error occurs, returns NULL
 */
unsigned char* js_upcall_get_form_encoding(void* data, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc;

	if (!js_ctx)internal("js_upcall_get_form_encoding called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return NULL; /* this isn't form */

	fc = jsint_find_object(fd, form_id);
	if (!fc)return NULL;

	switch (fc->method)
	{
		case FM_GET:
		case FM_POST:
			return stracpy("application/x-www-form-urlencoded");

		case FM_POST_MP:
			return stracpy("multipart/form-data");

		default:
			internal("Invalid form method!\n");
			return NULL;  /* never called, but GCC likes it */
	}
}


/* returns allocated string containing protocol from current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_protocol(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* p;
	int l;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));

	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	if (parse_url(loc, &l, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
	{
		mem_free(loc);
		return NULL;
	}
	p = memacpy(loc, l + 1); /* l is pointing to the colon, but we want protocol with colon */
	mem_free(loc);
	return p;
}


/* returns allocated string containing port of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_port(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* p;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));

	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	p = get_port_str(loc);
	mem_free(loc);
	return p;
}


/* returns allocated string containing hostname of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_hostname(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* p;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));

	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	p = get_host_name(loc);
	mem_free(loc);
	return p;
}


/* returns allocated string containing hostname and port of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_host(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* p, *h;
	int l1, l2;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, &h, &l1, NULL, &l2, NULL, NULL, NULL))
	{
		mem_free(loc);
		return NULL;
	}
	p = memacpy(h, l1 + l2);
	mem_free(loc);
	return p;
}


/* returns allocated string containing pathname of current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_pathname(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* d, *p;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL))
	{
		mem_free(loc);
		return NULL;
	}
	if (!d)
	{
		mem_free(loc);
		return NULL;
	}
	p = memacpy(d, strcspn(d, "?"));
	mem_free(loc);
	return p;
}


/* returns allocated string containing everything after ? in current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_search(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* d, *p;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL))
	{
		mem_free(loc);
		return NULL;
	}
	if (!d)
	{
		mem_free(loc);
		return NULL;
	}
	p = stracpy(strchr(d, '?'));
	mem_free(loc);
	return p;
}


/* returns allocated string containing everything between # and ? in current URL in the script context
 * on error (or there's no protocol) NULL is returned
 */
unsigned char* js_upcall_get_location_hash(void* data)
{
	struct f_data_c* fd;
	unsigned char* loc;
	unsigned char* d, *p;

	if (!data)internal("js_upcall_get_location called with NULL pointer!");
	fd = (struct f_data_c*)data;

	loc = mem_alloc(MAX_STR_LEN * sizeof(unsigned char));
	
	if (!(get_current_url(fd->ses, loc, MAX_STR_LEN)))
	{
		mem_free(loc);
		return NULL;
	}

	if (parse_url(loc, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &d, NULL, NULL))
	{
		mem_free(loc);
		return NULL;
	}
	if (!d)
	{
		mem_free(loc);
		return NULL;
	}
	d = strchr(d, '#');
	if (!d)
	{
		mem_free(loc);
		return NULL;
	}
	d++;
	p = memacpy(d, strcspn(d, "?"));
	mem_free(loc);
	return p;
}


/* returns allocated field of all form elements
 * size of the field will be stored in len
 * when an error occurs, returns NULL
 */
long* js_upcall_get_form_elements(void* data, long document_id, long form_id, int* len)
{
	struct f_data_c* js_ctx = (struct f_data_c*)data;
	struct f_data_c* fd;
	struct form_control* fc, *fc2;
	long* pole_Premysla_Zavorace;
	int b;

	if (!js_ctx)internal("js_upcall_get_form_elements called with NULL context pointer\n");
	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return NULL; /* this isn't form */
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return NULL;

	fc = jsint_find_object(fd, form_id);
	if (!fc)return NULL;

	*len = 0;

	foreach(fc2, fd->f_data->forms)
	if (fc2->form_num == fc->form_num)(*len)++;

	if (!(*len))return NULL;

	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	pole_Premysla_Zavorace = mem_alloc((*len) * sizeof(long));
	
	b = 0;
	foreachback(fc2, fd->f_data->forms)
	if (fc2->form_num == fc->form_num)
	{
		switch (fc2->type)
		{
			case FC_TEXT:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_TEXT;
				break;
			case FC_PASSWORD:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_PASSWORD;
				break;
			case FC_TEXTAREA:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_TEXTAREA;
				break;
			case FC_CHECKBOX:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_CHECKBOX;
				break;
			case FC_RADIO:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_RADIO;
				break;
			case FC_IMAGE:
			case FC_SELECT:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_SELECT;
				break;
			case FC_SUBMIT:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_SUBMIT ;
				break;
			case FC_RESET:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_RESET ;
				break;
			case FC_HIDDEN:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_HIDDEN ;
				break;
			case FC_BUTTON:
				pole_Premysla_Zavorace[b] = JS_OBJ_T_BUTTON ;
				break;
			default: /* internal("Invalid form element type.\n"); */
				(*len)--;
				continue;
		}
		pole_Premysla_Zavorace[b] |= ((fc2->g_ctrl_num) << JS_OBJ_MASK_SIZE);
		b++;
	}
	return pole_Premysla_Zavorace;
}


/* returns allocated field with anchors
 * size of the field is stored in len
 * when there're no anchors, *len is 0 and NULL is returned
 * on error NULL is returned
 */
long* js_upcall_get_anchors(void* hej_Hombre, long document_id, int* len)
{
	struct f_data_c* js_ctx = (struct f_data_c*)hej_Hombre;
	struct f_data_c* fd;
	struct tag* t;
	int a;
	long* to_je_Ono;
	*len = 0;

	if (!js_ctx)internal("js_upcall_get_anchors called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	if (!(fd->f_data))return NULL;
	foreach(t, fd->f_data->tags)(*len)++;
	if (!(*len))return NULL;
	if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
	to_je_Ono = mem_alloc((*len) * sizeof(long));
	
	a = 0;
	foreach(t, fd->f_data->tags)
	{
		to_je_Ono[a] = JS_OBJ_T_ANCHOR + (a << JS_OBJ_MASK_SIZE);
		a++;
	}
	return to_je_Ono;

}


/* returns whether radio or checkbox is checked
 * return value: 0=not checked
 *               1=checked
 *              -1=error
 */
int js_upcall_get_checkbox_radio_checked(void* smirak, long document_id, long radio_tv_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)smirak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	int state;

	if (!js_ctx)internal("js_upcall_get_checkbox_radio_checked called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;

	if ((radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_RADIO && (radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_CHECKBOX)return -1; /* this isn't radio nor TV */

	hopla = jsint_find_object(fd, radio_tv_id);
	if (!hopla)return -1;

	state = hopla->fs->state;
	mem_free(hopla);
	return state;
}


/* checks/unchecks radio or checkbox
 */
void js_upcall_set_checkbox_radio_checked(void* smirak, long document_id, long radio_tv_id, int value)
{
	struct f_data_c* js_ctx = (struct f_data_c*)smirak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;

	if (!js_ctx)internal("js_upcall_set_checkbox_radio_checked called with NULL context pointer\n");
	if ((radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_RADIO && (radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_CHECKBOX)return; /* this isn't radio nor TV */
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;

	hopla = jsint_find_object(fd, radio_tv_id);
	if (!hopla)return;

	hopla->fs->state = !!value;
	mem_free(hopla);
	redraw_document(fd);
}


/* returns whether radio or checkbox is checked
 * return value: 0=default not checked
 *     1=default checked
 *    -1=error
 */
int js_upcall_get_checkbox_radio_default_checked(void* bidak_smirak, long document_id, long radio_tv_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak_smirak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	int default_checked;

	if (!js_ctx)internal("js_upcall_get_checkbox_radio_default_checked called with NULL context pointer\n");
	if ((radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_RADIO && (radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_CHECKBOX)return -1; /* this isn't radio nor TV */
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;

	hopla = jsint_find_object(fd, radio_tv_id);
	if (!hopla)return -1;
	
	default_checked = hopla->fc->default_state;
	mem_free(hopla);
	return default_checked;
}


/* sets radio/checkbox default_checked in the form
 */
void js_upcall_set_checkbox_radio_default_checked(void* bidak_smirak, long document_id, long radio_tv_id, int value)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak_smirak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	int something_changed;
	value = !!value;

	if (!js_ctx)internal("js_upcall_set_checkbox_radio_default_checked called with NULL context pointer\n");
	if ((radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_RADIO && (radio_tv_id & JS_OBJ_MASK) != JS_OBJ_T_CHECKBOX)return; /* this isn't radio nor TV */
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return;

	hopla = jsint_find_object(fd, radio_tv_id);
	if (!hopla)return;
	
	something_changed = (hopla->fc->default_state) ^ value;
	hopla->fc->default_state = value;
	fd->f_data->uncacheable |= something_changed;
	mem_free(hopla);
}


/* returns allocated string with name of the form element
 * don't forget to free the string after use
 * on error returns NULL
 */
unsigned char* js_upcall_get_form_element_name(void* bidak, long document_id, long ksunt_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	unsigned char* hele_ho_bidaka;

	if (!js_ctx)internal("js_upcall_get_form_element_name called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
			break;

		default:
			return NULL;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)return NULL;
	
	hele_ho_bidaka = stracpy(hopla->fc->name);
	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets name of the form element
 * name is allocated string, this function deallocates it
 */
void js_upcall_set_form_element_name(void* bidak, long document_id, long ksunt_id, unsigned char* name)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;

	if (!js_ctx)internal("js_upcall_set_form_element_name called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))
	{
		if (name)mem_free(name);
		return;
	}

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
		case JS_OBJ_T_BUTTON:
			break;

		default:
			if (name) mem_free(name);
			return;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)
	{
		if (name)mem_free(name);
		return;
	}

	if ((name || (hopla->fc->name)) && strcmp(name, hopla->fc->name))
	{
		mem_free(hopla->fc->name);
		hopla->fc->name = stracpy(name);
		fd->f_data->uncacheable = 1;
	}
	mem_free(hopla);
	if (name) mem_free(name);
}


/* returns allocated string with value of VALUE attribute of the form element
 * on error returns NULL
 * don't forget to free the string after use
 */
unsigned char* js_upcall_get_form_element_default_value(void* bidak, long document_id, long ksunt_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	unsigned char* hele_ho_bidaka;
	struct conv_table* ct;

	if (!js_ctx)internal("js_upcall_get_form_element_default_value called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return NULL;

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
			break;

		default:
			return NULL;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)return NULL;
	
	ct = get_translation_table(fd->f_data->opt.cp, fd->f_data->cp);
	hele_ho_bidaka = convert_string(ct, hopla->fc->default_value, strlen(hopla->fc->default_value), NULL);

	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets attribute VALUE of the form element
 * name is allocated string that, this function frees it
 * when name is NULL default value will be empty
 */
void js_upcall_set_form_element_default_value(void* bidak, long document_id, long ksunt_id, unsigned char* name)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;

	if (!js_ctx)internal("js_upcall_set_form_element_default_value called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))
	{
		if (name)mem_free(name);
		return;
	}

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_SELECT:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_HIDDEN:
			break;

		default:
			if (name)mem_free(name);
			return;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)
	{
		if (name)mem_free(name);
		return;
	}

	if ((name || (hopla->fc->default_value)) && strcmp(name, hopla->fc->default_value))
	{
		struct conv_table* ct;

		mem_free(hopla->fc->default_value);
		ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
		hopla->fc->default_value = convert_string(ct, name, strlen(name), NULL);
		fd->f_data->uncacheable = 1;
	}
	mem_free(hopla);
	if (name)mem_free(name);
}

static unsigned char** get_js_event_ptr(struct js_event_spec** j, long type)
{
	create_js_event_spec(j);
	if (type == Conkeydown) return &(*j)->keydown_code;
	else if (type == Conkeypress) return &(*j)->keypress_code;
	else if (type == Conkeyup) return &(*j)->keyup_code;
	else return NULL;
}

void js_upcall_set_form_element_event_handler(void* bidak, long document_id, long ksunt_id, long type, unsigned char* name)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	int i;

	if (!js_ctx)internal("js_upcall_set_form_element_event_handler called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))
	{
		if (name)mem_free(name);
		return;
	}
	if ((ksunt_id & JS_OBJ_MASK) == JS_OBJ_T_FRAME || (ksunt_id & JS_OBJ_MASK) == JS_OBJ_T_DOCUMENT)
	{
		unsigned char** p = get_js_event_ptr(&fd->f_data->js_event, type);
		if (!p)
		{
			mem_free(name);
			return;
		}
		if (*p) mem_free(*p);
		*p = name;
		fd->f_data->uncacheable = 1;
		return;
	}
	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)
	{
		if (name)mem_free(name);
		return;
	}
	for (i = 0; i < fd->f_data->nlinks; i++)
	{
		struct link* l = &fd->f_data->links[i];
		if (l->form == hopla->fc)
		{
			unsigned char** p = get_js_event_ptr(&l->js_event, type);
			mem_free(hopla);
			if (!p)
			{
				mem_free(name);
				return;
			}
			if (*p) mem_free(*p);
			*p = name;
			fd->f_data->uncacheable = 1;
			return;
		}
	}
	mem_free(hopla);
	mem_free(name);
}


/* returns allocated string with actual value of password, text or textarea element
 * on error returns NULL
 * don't forget to free the string after use
 */
unsigned char* js_upcall_get_form_element_value(void* bidak, long document_id, long ksunt_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	unsigned char* hele_ho_bidaka;
	struct conv_table* ct;

	if (!js_ctx)internal("js_upcall_get_form_element_value called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return NULL;

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
			break;

		default:
			return NULL;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)return NULL;

	ct = get_translation_table(fd->f_data->opt.cp, fd->f_data->cp);
	hele_ho_bidaka = convert_string(ct, hopla->fs->value, strlen(hopla->fs->value), NULL);

	mem_free(hopla);
	return hele_ho_bidaka;
}


/* sets actual value of password, text or textarea element
 * name is allocated string that, this function frees it
 * when name is NULL default value will be empty
 */
void js_upcall_set_form_element_value(void* bidak, long document_id, long ksunt_id, unsigned char* name)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	struct conv_table* ct;

	if (!js_ctx)internal("js_upcall_set_form_element_value called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))
	{
		if (name)mem_free(name);
		return;
	}

	fd = jsint_find_document(document_id);

	switch (ksunt_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
			break;

		default:
			if (name)mem_free(name);
			return;   /* To neni Jim Beam! */
	}

	hopla = jsint_find_object(fd, ksunt_id);
	if (!hopla)
	{
		if (name)mem_free(name);
		return;
	}

	mem_free(hopla->fs->value);
	ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
	hopla->fs->value = convert_string(ct, name, strlen(name), NULL);

	if ((size_t)hopla->fs->state > strlen(hopla->fs->value))
		hopla->fs->state = strlen(hopla->fs->value);
	if ((ksunt_id & JS_OBJ_MASK) != JS_OBJ_T_TEXTAREA)
	{
		if ((size_t)hopla->fs->vpos > strlen(hopla->fs->value))
			hopla->fs->vpos = strlen(hopla->fs->value);
	}
	mem_free(hopla);
	if (name)mem_free(name);
	redraw_document(fd);
}


/* emulates click on everything */
void js_upcall_click(void* bidak, long document_id, long elem_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;

	if (!js_ctx)internal("js_upcall_click called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;

	switch (elem_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_CHECKBOX:
		case JS_OBJ_T_RADIO:
		case JS_OBJ_T_SUBMIT:
		case JS_OBJ_T_RESET:
		case JS_OBJ_T_BUTTON:
			{
				struct hopla_mladej* hopla;
				int a;
				struct link* l;

				if (!fd->f_data)return;
				hopla = jsint_find_object(fd, elem_id);
				if (!hopla)return;

				for (a = 0; a < fd->f_data->nlinks; a++)
				{
					l = &(fd->f_data->links[a]);
					if (l->form && l->form == hopla->fc) /* to je on! */
					{
						int old_link = fd->vs->current_link;
						int old_orig_link = fd->vs->orig_link;
						fd->vs->current_link = a;
						fd->vs->orig_link = a;
						enter(fd->ses, fd, 0);
						draw_fd(fd);
						fd->vs->current_link = old_link;
						fd->vs->orig_link = old_orig_link;
						change_screen_status(fd->ses);
						print_screen_status(fd->ses);
						break;
					}
				}
				mem_free(hopla);
			}
			break;
	}
}

#ifdef G
static int find_go_link_num;
static struct g_object* to_je_on_bidak;
static void find_go(struct g_object* p, struct g_object* c)
{
	if (c->draw == (void (*)(struct f_data_c*, struct g_object*, int, int))g_text_draw)
		if (((struct g_object_text*)c)->link_num == find_go_link_num)
		{
			to_je_on_bidak = c;
			return;
		}
	if (c->get_list)c->get_list(c, find_go);
}
#endif

/* emulates focus on password, text and textarea */
void js_upcall_focus(void* bidak, long document_id, long elem_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;

	if (!js_ctx)internal("js_upcall_focus called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;

	switch (elem_id & JS_OBJ_MASK)
	{
		case JS_OBJ_T_TEXT:
		case JS_OBJ_T_PASSWORD:
		case JS_OBJ_T_TEXTAREA:
			{
				struct hopla_mladej* hopla;
				int a;
				struct link* l;

				if (!fd->f_data)return;
				hopla = jsint_find_object(fd, elem_id);
				if (!hopla)return;

				for (a = 0; a < fd->f_data->nlinks; a++)
				{
					l = &(fd->f_data->links[a]);
					if (l->form && l->form == hopla->fc) /* to je on! */
					{
						struct session* ses = fd->ses;
						int x = 0;
						while (fd != current_frame(ses)) next_frame(ses, 1), x = 1;
						fd->vs->current_link = a;
						fd->vs->orig_link = a;
						if (fd->ses->term->spec->braille)
						{
							if (fd->f_data->links[a].n)
							{
								fd->vs->brl_x = fd->vs->orig_brl_x = fd->f_data->links[a].pos[0].x;
								fd->vs->brl_y = fd->vs->orig_brl_y = fd->f_data->links[a].pos[0].y;
							}
						}
#ifdef G
						if (F)
						{
							fd->ses->locked_link = 1;
							to_je_on_bidak = NULL;
							find_go_link_num = a;

							/* tak tedka tu budu carovat g_object_text, kterej patri k tomuhle linku */
							if (fd->f_data->root->get_list)fd->f_data->root->get_list(fd->f_data->root, find_go);
							fd->f_data->locked_on = to_je_on_bidak;
						}
#endif
						if (l->js_event && l->js_event->focus_code)
							jsint_execute_code(fd, l->js_event->focus_code, strlen(l->js_event->focus_code), -1, -1, -1, NULL);

						/*draw_fd(fd);*/
						draw_formatted(ses);
						change_screen_status(fd->ses);
						print_screen_status(fd->ses);
						break;
					}
				}
				mem_free(hopla);
			}
			break;
	}
}

/* emulates focus on password, text and textarea */
void js_upcall_blur(void* bidak, long document_id, long elem_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;

	if (!js_ctx)internal("js_upcall_blur called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;

	/* in text mode do nothing, because we don't know where to go with cursor */
#ifdef G
	if (F)
		switch (elem_id & JS_OBJ_MASK)
		{
			case JS_OBJ_T_TEXT:
			case JS_OBJ_T_PASSWORD:
			case JS_OBJ_T_TEXTAREA:
				{
					struct hopla_mladej* hopla;
					int a;
					struct link* l;

					if (!fd->f_data)return;
					hopla = jsint_find_object(fd, elem_id);
					if (!hopla)return;

					for (a = 0; a < fd->f_data->nlinks; a++)
					{
						l = &(fd->f_data->links[a]);
						if (l->form && l->form == hopla->fc) /* to je on! */
						{
							fd->ses->locked_link = 0;
							if (l->js_event && l->js_event->blur_code)
								jsint_execute_code(fd, l->js_event->blur_code, strlen(l->js_event->blur_code), -1, -1, -1, NULL);

							/* pro jistotu */
							draw_fd(fd);
							change_screen_status(fd->ses);
							print_screen_status(fd->ses);
							break;
						}
					}
					mem_free(hopla);
				}
				break;
		}
#endif
}

/* emulates submit of a form */
void js_upcall_submit(void* bidak, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;
	struct form_control* form;
	int has_onsubmit;
	unsigned char* u;

	if (!js_ctx)internal("js_upcall_submit called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;

	if (fd->ses->rq && fd->ses->defered_url) return;

	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return;
	form = jsint_find_object(fd, form_id);
	if (!form)return;

	u = get_form_url(fd->ses, fd, form, &has_onsubmit);
	if (u)
	{
		goto_url_f(fd->ses, NULL, u, NULL, fd, form->form_num, has_onsubmit, 0, 0);
		mem_free(u);
	}
	draw_fd(fd);
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}


/* emulates reset of a form */
void js_upcall_reset(void* bidak, long document_id, long form_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)bidak;
	struct f_data_c* fd;

	if (!js_ctx)internal("js_upcall_reset called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return;
	if ((form_id & JS_OBJ_MASK) != JS_OBJ_T_FORM)return;
	if (!fd->f_data)return;

	reset_form(fd, form_id >> JS_OBJ_MASK_SIZE);
	draw_fd(fd);
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}

/* returns length (number of radio buttons) of a radio
 * on error returns -1
 */
int js_upcall_get_radio_length(void* p, long document_id, long radio_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)p;
	struct f_data_c* fd;
	struct form_control* f;
	struct hopla_mladej* hopla;
	struct form_control* radio;
	int count = 0;

	if (!js_ctx)internal("js_upcall_get_radio_length called with NULL context pointer\n");
	if ((radio_id & JS_OBJ_MASK) != JS_OBJ_T_RADIO) return -1;
	fd = jsint_find_document(document_id);
	if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return -1;

	hopla = jsint_find_object(fd, radio_id);
	if (!hopla)return -1;
	radio = hopla->fc;

	/* find form elements with the same type, form_num (belonging to the same form) and name */
	foreachback(f, fd->f_data->forms)
	if (f->type == radio->type && f->form_num == radio->form_num && !strcmp(radio->name, f->name))count++;
	mem_free(hopla);
	return count;
}

/* returns number of items in a select form element
 * on error returns -1
 */
int js_upcall_get_select_length(void* p, long document_id, long select_id)
{
	int l;
	struct f_data_c* js_ctx = (struct f_data_c*)p;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;

	if (!js_ctx)internal("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id & JS_OBJ_MASK) != JS_OBJ_T_SELECT) return -1;
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;

	hopla = jsint_find_object(fd, select_id);
	if (!hopla)return -1;

	l = hopla->fc->nvalues;
	mem_free(hopla);
	return l;
}


/* returns allocated field of select items
 * don't forget to free: text and value of each item and the field
 * on error returns NULL
 * n is number of items in the field
 */
struct js_select_item* js_upcall_get_select_options(void* p, long document_id, long select_id, int* n)
{
	struct f_data_c* js_ctx = (struct f_data_c*)p;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	struct js_select_item* elektricke_pole;
	int ukazme_si_na_nej;

	*n = 0;
	if (!js_ctx)internal("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id& JS_OBJ_MASK) != JS_OBJ_T_SELECT) return NULL;
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	hopla = jsint_find_object(fd, select_id);
	if (!hopla)return NULL;

	*n = hopla->fc->nvalues;
	if ((unsigned)*n > MAXINT / sizeof(struct js_select_item)) overalloc();
	elektricke_pole = mem_alloc((*n)*sizeof(struct js_select_item));

	for (ukazme_si_na_nej = 0; ukazme_si_na_nej < (*n); ukazme_si_na_nej++)
	{
		elektricke_pole[ukazme_si_na_nej].text = stracpy((hopla->fc->labels)[ukazme_si_na_nej]);
		elektricke_pole[ukazme_si_na_nej].value = stracpy((hopla->fc->values)[ukazme_si_na_nej]);
		elektricke_pole[ukazme_si_na_nej].selected = (ukazme_si_na_nej == (hopla->fs->state));
		elektricke_pole[ukazme_si_na_nej].default_selected = (ukazme_si_na_nej == (hopla->fc->default_state));
	}
	mem_free(hopla);
	return elektricke_pole;
}

/* returns index of just selected item in a select form element
 * on error returns -1
 */
int js_upcall_get_select_index(void* p, long document_id, long select_id)
{
	struct f_data_c* js_ctx = (struct f_data_c*)p;
	struct f_data_c* fd;
	struct hopla_mladej* hopla;
	int l;

	if (!js_ctx)internal("js_upcall_get_select_length called with NULL context pointer\n");
	if ((select_id & JS_OBJ_MASK) != JS_OBJ_T_SELECT) return -1;
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;

	hopla = jsint_find_object(fd, select_id);
	if (!hopla)return -1;

	l = hopla->fs->state;
	mem_free(hopla);
	return l;
}


struct gimme_js_id_string
{
	long id;
	long js_id;
	unsigned char* string;
	signed int n;
};

/* open a link in a new xterm */
void send_vodevri_v_novym_vokne(struct terminal* term, void (*open_window)(struct terminal* term, unsigned char*, unsigned char*), struct session* ses)
{
	if (ses->dn_url)
	{
		unsigned char* enc_url = encode_url(ses->dn_url);
		open_window(term, path_to_exe, enc_url);
		mem_free(enc_url);
	}
}

/* aux function for js_upcall_goto_url */
static void js_upcall_goto_url_ok_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id_string* jsid = (struct gimme_js_id_string*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	/* it doesn't matter, that fd->js is NULL */
	if (jsid->n && can_open_in_new(fd->ses->term)) /* open in new window */
	{
		if (fd->ses->dn_url) mem_free(fd->ses->dn_url);
		fd->ses->dn_url = stracpy(jsid->string);
		open_in_new_window(fd->ses->term, send_vodevri_v_novym_vokne, fd->ses);
	}
	else
		goto_url(fd->ses, jsid->string);

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* aux function for js_upcall_goto_url */
static void js_upcall_goto_url_cancel_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id* jsid = (struct gimme_js_id*)data;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */

	if (!(fd->js) || jsid->js_id != fd->js->ctx->js_id)return;
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
}


/* gets struct fax_me_tender_int_string */
/* asks user whether to go to the url or not */
/* structure and the text are both deallocated */
/* must be called from select loop */
/* if num in fax_me_tender_int_string is not null, open in a new window */
void js_upcall_goto_url(void* data)
{
	struct fax_me_tender_int_string* s = (struct fax_me_tender_int_string*)data;
	struct f_data_c* fd;
	struct terminal* term;
	unsigned char* dest_url;
	int in_new_win;

	fd = (struct f_data_c*)(s->ident);
	term = fd->ses->term;

	if (!fd->js) return;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	if (!s)internal("js_upcall_goto_url called with NULL pointer\n");

	if (!s->string)
	{
		js_mem_free(data);
		goto goto_url_failed;
	}
	if (fd->loc && fd->loc->url) dest_url = join_urls(fd->loc->url, s->string);
	else dest_url = stracpy(s->string);
	if (!(dest_url))
	{
		js_mem_free(s->string);
		js_mem_free(data);
		goto goto_url_failed;
	}
	js_mem_free(s->string);
	in_new_win = s->num;

	if (js_manual_confirmation)
	{
		struct gimme_js_id_string* jsid;

		/* goto the same url */
		{
			unsigned char txt[MAX_STR_LEN];
			void* p;

			p = get_current_url(fd->ses, txt, MAX_STR_LEN);
			if (p && fd->loc && fd->loc->url && !strcmp(txt, dest_url))
			{
				mem_free(dest_url);
				js_mem_free(data);
				goto goto_url_failed;
			}
		}

		jsid = mem_alloc(sizeof(struct gimme_js_id_string));
		
		/* context must be a valid pointer ! */
		/* fill in jsid */
		jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
		jsid->js_id = fd->js->ctx->js_id;
		jsid->string = dest_url;
		jsid->n = s->num;
		
		msg_box(
		  term,   /* terminal */
		  getml(jsid->string, jsid, NULL), /* memory blocks to free */
		  TEXT(T_GOTO_URL),   /* title */
		  AL_CENTER | AL_EXTD_TEXT, /* alignment */
		  jsid->n ? TEXT(T_JS_IS_ATTEMPTING_TO_OPEN_NEW_WINDOW_WITH_URL) : TEXT(T_JS_IS_ATTEMPTING_TO_GO_TO_URL), " \"", jsid->string, "\".", NULL, /* message */
		  jsid,   /* data for button functions */
		  3,   /* # of buttons */
		  TEXT(T_ALLOW), js_upcall_goto_url_ok_pressed, B_ENTER,
		  TEXT(T_REJECT), js_upcall_goto_url_cancel_pressed, B_ESC,
		  TEXT(T_KILL_SCRIPT), js_kill_script_pressed, NULL /* dirty trick: gimme_js_id_string and gimme_js_id begins with the same long */
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		if (in_new_win && can_open_in_new(fd->ses->term)) /* open in new window */
		{
			if (fd->ses->dn_url) mem_free(fd->ses->dn_url);
			fd->ses->dn_url = stracpy(dest_url);
			open_in_new_window(fd->ses->term, send_vodevri_v_novym_vokne, fd->ses);
		}
		else
			goto_url(fd->ses, dest_url);
		js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
		mem_free(dest_url);
	}
	return;

goto_url_failed:
	js_downcall_vezmi_null(fd->js->ctx);   /* call downcall */
	return;
}


/* returns number of items in history */
int js_upcall_get_history_length(void* context)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	struct location* l;
	int len = 0;

	if (!fd)internal("PerMe, PerMe, ja si te podam!\n");

	foreach(l, fd->ses->history)len++;

	return len;
}


/* aux function for js_upcall_goto_history */
static void js_upcall_goto_history_ok_pressed(void* data)
{
	struct f_data_c* fd;
	struct gimme_js_id_string* jsid = (struct gimme_js_id_string*)data;
	struct location* loc;
	int a;

	fd = jsint_find_document(jsid->id);
	if (!fd)return;  /* context no longer exists */
	
	a = 0;
	foreach(loc, fd->ses->history)a++;

	if (a < jsid->n && (fd->js) && jsid->js_id == fd->js->ctx->js_id)
	{
		js_downcall_vezmi_null(fd->js->ctx);  /* call downcall */
		return;
	}

	go_backwards(fd->ses->term, (void*)(my_intptr_t)(jsid->n), fd->ses);
}


/* gets struct fax_me_tender_int_string
 * either num or string is set, but not both, the other must be NULL
 * asks user whether to go to the url or not
 * structure and the text are both deallocated
 * must be called from select loop
 * number can be:
 *    >0  go forward in history (not supported)
 *     0  do nothing (means use string)
 *    <0  go backward in history (supported :) )
 * if string is defined - find appropriate history item and go to the url, when
 * the URL doesn't exist do nothing
 *
 * JAK TO FUNGUJE:
 * string se prekonvertuje na cislo (projde se historie)
 * po zmacknuti OK se spocita delka historie a pokud je dostatecna, n-krat
 * zavola go_back. Pokud neni, tak se chovame jako pri cancelu.
 */

void js_upcall_goto_history(void* data)
{
	struct fax_me_tender_int_string* s = (struct fax_me_tender_int_string*)data;
	struct f_data_c* fd;
	struct terminal* term;
	unsigned char* url = NULL;
	unsigned char txt[16];
	long history_num = 0;

	/* context must be a valid pointer ! */
	fd = (struct f_data_c*)(s->ident);

	if (!fd->js) return;

	/* kill timer, that called me */
	js_spec_vykill_timer(fd->js->ctx, 0);

	if (!s)internal("Hele, tyhle prasarny si zkousej na nekoho jinyho, jo?!\n");
	if (!(s->num) && !(s->string))internal("Tak tohle na mne nezkousej, bidaku!\n");
	if ((s->num) && (s->string))internal("Ta sedla!\n");

	/* find the history item */
	if (s->num) /* goto n-th item */
	{
		struct location* loc;
		int a = 0;

		if ((s->num) > 0)
		{
			if (s->string)js_mem_free(s->string);  /* forward not supported */
			js_mem_free(data);
			goto goto_history_failed;
		}
		s->num = -s->num;
		history_num = s->num;

		foreach(loc, fd->ses->history)
		{
			if (a == s->num)
			{
				url = stracpy(loc->url);
				break;
			}
			a++;
		}
	}
	else  /* goto given url */
	{
		struct location* loc;
		int a = 0;

		foreach(loc, fd->ses->history)
		{
			if (!strcmp(s->string, loc->url))
			{
				url = stracpy(s->string);
				history_num = a;
				break;
			}
			a++;
		}
	}

	if (s->string)js_mem_free(s->string);
	if (!url)
	{
		js_mem_free(data);
		goto goto_history_failed;
	}

	term = fd->ses->term;

	if (js_manual_confirmation)
	{
		struct gimme_js_id_string* jsid;

		jsid = mem_alloc(sizeof(struct gimme_js_id_string));

		/* fill in jsid */
		jsid->id = ((fd->id) << JS_OBJ_MASK_SIZE) | JS_OBJ_T_DOCUMENT;
		jsid->js_id = fd->js->ctx->js_id;
		jsid->string = url;
		jsid->n = history_num;

		snprintf(txt, 16, " (-%d) ", jsid->n);
		msg_box(
		  term,   /* terminal */
		  getml(url, jsid, NULL), /* memory blocks to free */
		  TEXT(T_GOTO_HISTORY),   /* title */
		  AL_CENTER | AL_EXTD_TEXT, /* alignment */
		  TEXT(T_JS_IS_ATTEMPTING_TO_GO_INTO_HISTORY), txt, TEXT(T_TO_URL), " \"", url, "\".", NULL, /* message */
		  jsid,   /* data for button functions */
		  3,   /* # of buttons */
		  TEXT(T_ALLOW), js_upcall_goto_history_ok_pressed, B_ENTER,
		  TEXT(T_REJECT), js_upcall_goto_url_cancel_pressed, B_ESC,
		  TEXT(T_KILL_SCRIPT), js_kill_script_pressed, NULL /* dirty trick: gimme_js_id_string and gimme_js_id begins with the same long */
		);
		js_mem_free(s);
	}
	else
	{
		js_mem_free(s);
		mem_free(url);
		go_backwards(term, (void*)(history_num), fd->ses);
	}
	return;
goto_history_failed:
	js_downcall_vezmi_null(fd->js->ctx);
	return;
}


/* set default status-line text
 * tak_se_ukaz_Kolbene is allocated string or NULL
 */
void js_upcall_set_default_status(void* context, unsigned char* tak_se_ukaz_Kolbene)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	unsigned char* trouba;

	if (!fd)internal("Tak tohle teda ne, bobanku!\n");

	if (!(*tak_se_ukaz_Kolbene))
	{
		mem_free(tak_se_ukaz_Kolbene);  /* Ale to hlavni jsme se nedozvedeli - s tim chrapanim jste mi neporadil... */
		tak_se_ukaz_Kolbene = NULL;
	}

	if (fd->ses->default_status)mem_free(fd->ses->default_status);
	skip_nonprintable(tak_se_ukaz_Kolbene);
	if (fd->f_data && tak_se_ukaz_Kolbene)
	{
		struct conv_table* ct; /* ... a ted ty pochybne reci o majetku ... */
		
		ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
		trouba = convert_string(ct, tak_se_ukaz_Kolbene, strlen(tak_se_ukaz_Kolbene), NULL); /* Taky to mate levnejsi - jinak by to stalo deset! */
		mem_free(tak_se_ukaz_Kolbene);
		/* a je to v troube... */
	}
	else
	{
		trouba = tak_se_ukaz_Kolbene;
	}

	fd->ses->default_status = trouba;
	change_screen_status(fd->ses);
	print_screen_status(fd->ses);
}


/* returns allocated string with default status-line value or NULL when default value is empty
 */
unsigned char* js_upcall_get_default_status(void* context)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	unsigned char* tak_se_ukaz_Danku = NULL;
	unsigned char* trouba;

	if (!fd)internal("Ale hovno!\n");
	
	if (fd->ses->default_status && (*fd->ses->default_status))tak_se_ukaz_Danku = stracpy(fd->ses->default_status);
	skip_nonprintable(tak_se_ukaz_Danku);
	if (fd->f_data && tak_se_ukaz_Danku)
	{
		struct conv_table* ct;
		
		ct = get_translation_table(fd->f_data->opt.cp, fd->f_data->cp);
		trouba = convert_string(ct, tak_se_ukaz_Danku, strlen(tak_se_ukaz_Danku), NULL);
		mem_free(tak_se_ukaz_Danku);
	}
	else
	{
		trouba = tak_se_ukaz_Danku;
	}

	/* Tak to mame Kolben a Danek po peti korunach... */
	
	return trouba; /* No jo, je to v troube! */
}


/* set status-line text
 * tak_se_ukaz_Kolbene is allocated string or NULL
 */
void js_upcall_set_status(void* context, unsigned char* tak_se_ukaz_Kolbene)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	unsigned char* trouba;

	if (!fd)internal("To leda tak -PRd!\n");

	if (!(*tak_se_ukaz_Kolbene))
	{
		mem_free(tak_se_ukaz_Kolbene);
		tak_se_ukaz_Kolbene = NULL;
	}

	if (fd->ses->st)mem_free(fd->ses->st);
	skip_nonprintable(tak_se_ukaz_Kolbene);
	if (fd->f_data && tak_se_ukaz_Kolbene)
	{
		struct conv_table* ct;
		
		ct = get_translation_table(fd->f_data->cp, fd->f_data->opt.cp);
		trouba = convert_string(ct, tak_se_ukaz_Kolbene, strlen(tak_se_ukaz_Kolbene), NULL);
		mem_free(tak_se_ukaz_Kolbene);
		/* a je to v troube... */
	}
	else
	{
		trouba = tak_se_ukaz_Kolbene;
	}

	fd->ses->st = trouba;
	print_screen_status(fd->ses);
}


/* returns allocated string with default status-line value or NULL when default value is empty
 */
unsigned char* js_upcall_get_status(void* context)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	unsigned char* tak_se_ukaz_Danku = NULL;
	unsigned char* trouba;

	if (!fd)internal("To leda tak hovno!\n");
	
	if (fd->ses->st && (*fd->ses->st))tak_se_ukaz_Danku = stracpy(fd->ses->st);
	skip_nonprintable(tak_se_ukaz_Danku);
	if (fd->f_data && tak_se_ukaz_Danku)
	{
		struct conv_table* ct;
		
		ct = get_translation_table(fd->f_data->opt.cp, fd->f_data->cp);
		trouba = convert_string(ct, tak_se_ukaz_Danku, strlen(tak_se_ukaz_Danku), NULL);
		mem_free(tak_se_ukaz_Danku);
	}
	else
	{
		trouba = tak_se_ukaz_Danku;
	}

	/* Kolben a Danek, to mame po peti korunach... */
	
	return trouba;
}

/* returns allocated string with cookies, or NULL on error */
unsigned char* js_upcall_get_cookies(void* context)
{
	struct f_data_c* fd = (struct f_data_c*)context;
	unsigned char* s = init_str();
	int l = 0;
	int nc = 0;
	struct cookie* c, *d;
	unsigned char* server, *data;
	struct c_domain* cd;

	if (!fd)internal("Tak tomu rikam selhani komunikace...\n");

	/* zavolame set_cookies, ten zparsuje fd->js->ctx->cookies a necha tam nezparsovatelnej zbytek */

	if (!fd->js || !fd->js->ctx)
	{
		mem_free(s);
		return NULL;
	}
	if (!fd->rq) goto ty_uz_se_nevratis;
	
	jsint_set_cookies(fd, 0);
	
	server = get_host_name(fd->rq->url);
	data = get_url_data(fd->rq->url);

	if (data > fd->rq->url) data--;
	foreach(cd, c_domains) if (is_in_domain(cd->domain, server)) goto ok;
	mem_free(server);
ty_uz_se_nevratis:
	if (fd->js->ctx->cookies)add_to_str(&s, &l, fd->js->ctx->cookies);
	else
	{
		mem_free(s);
		s = NULL;
	}
	return s;
ok:
	foreach(c, cookies) if (is_in_domain(c->domain, server)) if (is_path_prefix(c->path, data))
		{
			if (cookie_expired(c))
			{
				d = c;
				c = c->prev;
				del_from_list(d);
				free_cookie(d);
				mem_free(d);
				continue;
			}
			if (c->secure) continue;
			if (!nc) nc = 1;
			else add_to_str(&s, &l, "; ");
			add_to_str(&s, &l, c->name);
			if (c->value)
			{
				add_to_str(&s, &l, "=");
				add_to_str(&s, &l, c->value);
			}
		}

	if (!nc)
	{
		mem_free(s);
		s = NULL;
	}
	mem_free(server);

	/* za strinzik sestaveny z vnitrni reprezentace susenek jeste prilepime nezparsovatelnej zbytek */
	if (fd->js->ctx->cookies)
	{
		if (!s)s = stracpy(fd->js->ctx->cookies);
		else
		{
			add_to_str(&s, &l, "; ");
			add_to_str(&s, &l, fd->js->ctx->cookies);
		}
	}
	/*debug("get_cookies: \"%s\"", s);*/
	return s;
}

/* FIXME: document.all nechodi, musi se prepsat, aby vracel dvojice frame:idcko */


/* adds all in given f_data_c, the f_data_c must be accessible by the javascript */
void add_all_recursive_in_fd(long** field, int* len, struct f_data_c* fd, struct f_data_c* js_ctx)
{
	struct f_data_c* ff;
	struct form_control* fc;
	
#ifdef G
	struct xlist_head* fi;
#endif

	/* add all accessible frames */
	foreach(ff, fd->subframes)
	if (jsint_can_access(js_ctx, ff))
		if (!((*field) = add_fd_id(*field, len, js_upcall_get_frame_id(fd), js_upcall_get_frame_id(ff), ff->f_data ? ff->f_data->opt.framename : NULL)))return;

	if (!(fd->f_data))goto tady_uz_nic_peknyho_nebude;

#ifdef G
	/* add all images */
	if (F)
		foreach(fi, fd->f_data->images)
	{
		struct g_object_image* gi;
		struct g_object_image goi;

		gi = (struct g_object_image*)((char*)fi + ((char*)(&goi) - (char*)(&(goi.image_list))));
		if (!((*field) = add_fd_id(*field, len, js_upcall_get_frame_id(fd), JS_OBJ_T_IMAGE + ((gi->id) << JS_OBJ_MASK_SIZE), gi->name)))return;
	}
#endif
	/* add all forms */
	foreachback(fc, fd->f_data->forms)
	if (!((*field) = add_fd_id(*field, len, js_upcall_get_frame_id(fd), ((fc->form_num) << JS_OBJ_MASK_SIZE) + JS_OBJ_T_FORM, fc->form_name)))return;

	/* add all form elements */
	foreachback(fc, fd->f_data->forms)
	{
		long tak_mu_to_ukaz = 0;
		tak_mu_to_ukaz = (fc->g_ctrl_num) << JS_OBJ_MASK_SIZE;
		switch (fc->type)
		{
			case FC_TEXT:
				tak_mu_to_ukaz |= JS_OBJ_T_TEXT;
				break;
			case FC_PASSWORD:
				tak_mu_to_ukaz |= JS_OBJ_T_PASSWORD;
				break;
			case FC_TEXTAREA:
				tak_mu_to_ukaz |= JS_OBJ_T_TEXTAREA;
				break;
			case FC_CHECKBOX:
				tak_mu_to_ukaz |= JS_OBJ_T_CHECKBOX;
				break;
			case FC_RADIO:
				tak_mu_to_ukaz |= JS_OBJ_T_RADIO;
				break;
			case FC_IMAGE:
			case FC_SELECT:
				tak_mu_to_ukaz |= JS_OBJ_T_SELECT;
				break;
			case FC_SUBMIT:
				tak_mu_to_ukaz |= JS_OBJ_T_SUBMIT ;
				break;
			case FC_RESET:
				tak_mu_to_ukaz |= JS_OBJ_T_RESET ;
				break;
			case FC_HIDDEN:
				tak_mu_to_ukaz |= JS_OBJ_T_HIDDEN ;
				break;
			case FC_BUTTON:
				tak_mu_to_ukaz |= JS_OBJ_T_BUTTON ;
				break;
			default:/* internal("Invalid form element type.\n"); */
				tak_mu_to_ukaz = 0;
				break;
		}
		if (tak_mu_to_ukaz && !((*field) = add_fd_id(*field, len, js_upcall_get_frame_id(fd), tak_mu_to_ukaz, fc->name)))return;
	}
	
tady_uz_nic_peknyho_nebude:

	foreach(ff, fd->subframes)
	if (jsint_can_access(js_ctx, ff)) add_all_recursive_in_fd(field, len, ff, js_ctx);
}

/* returns allocated field of all objects in the document (document.all)
 * size of the field will be stored in len
 * the field has 3x more items than the number of objects
 * field[x+0]==id of frame
 * field[x+1]==id of the object
 * field[x+2]==allocated unsigned char* with name of the object or NULL (when there's no name)
 *
 * when an error occurs, returns NULL
 */
long* js_upcall_get_all(void* chuligane, long document_id, int* len)
{
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	long* pole_neorane; /* Premysle Zavoraci, kde se flakas? Zase forbesis, co? */
	struct f_data_c* fd;

	if (!js_ctx)internal("js_upcall_get_all called with NULL context pointer\n");
	fd = jsint_find_document(document_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	*len = 0;

	pole_neorane = mem_alloc(sizeof(long));

	add_all_recursive_in_fd(&pole_neorane, len, fd, js_ctx);

	/* nothing was found */
	if (!pole_neorane)return NULL;
	if (!(*len))mem_free(pole_neorane), pole_neorane = NULL;
	
	return pole_neorane;
}


/* returns allocated field of all images
 * size of the field will be stored in len
 * when an error occurs, returns NULL
 */

long* js_upcall_get_images(void* chuligane, long document_id, int* len)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	long* pole_Premysla_Zavorace;
	struct xlist_head* fi;
	int a;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_images called with NULL context pointer\n");
		fd = jsint_find_document(document_id);
		if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return NULL;

		*len = 0;
		
		foreach(fi, fd->f_data->images)(*len)++;

		if (!(*len))return NULL;
		
		if ((unsigned)*len > MAXINT / sizeof(long)) overalloc();
		pole_Premysla_Zavorace = mem_alloc((*len) * sizeof(long));
		
		a = 0;
		foreachback(fi, fd->f_data->images)
		{
			unsigned id;
			struct g_object_image gi;

			id = ((struct g_object_image*)((char*)fi + ((char*)&gi - (char*) & (gi.image_list))))->id;

			pole_Premysla_Zavorace[a] = JS_OBJ_T_IMAGE + (id << JS_OBJ_MASK_SIZE);
			a++;
		}
		return pole_Premysla_Zavorace;
	}
	else
#endif
	{
		document_id = document_id;
		*len = 0;
		return NULL;
	}
}

/* returns width of given image or -1 on error */
int js_upcall_get_image_width(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct f_data_c* fd;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_width called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		return gi->xw;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns height of given image or -1 on error */
int js_upcall_get_image_height(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct f_data_c* fd;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_height called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		return gi->yw;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns border of given image or -1 on error */
int js_upcall_get_image_border(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_border called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		return gi->border;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns vspace of given image or -1 on error */
int js_upcall_get_image_vspace(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_vspace called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		return gi->vspace;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns hspace of given image or -1 on error */
int js_upcall_get_image_hspace(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_hspace called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		return gi->hspace;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns allocated string with name of given image or NULL on error */
unsigned char* js_upcall_get_image_name(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_name called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return NULL;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return NULL;
		
		gi = jsint_find_object(fd, image_id);

		if (!gi)return NULL;
		
		return stracpy(gi->name);
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return NULL;
	}
}


/* returns allocated string with name of given image or NULL on error */
unsigned char* js_upcall_get_image_alt(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_alt called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return NULL;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return NULL;
		
		return stracpy(gi->alt);
	}
	else
#endif
	{
		chuligane = chuligane;
		document_id = document_id;
		image_id = image_id;
		return NULL;
	}
}


/* sets image name to given value */
/* name is deallocated after use with mem_free */
void js_upcall_set_image_name(void* chuligane, long document_id, long image_id, unsigned char* name)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_set_image_name called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return;
		
		if (gi->name)mem_free(gi->name);
		gi->name = stracpy(name); /* radeji takhle, protoze to je bezpecnejsi: az PerM zase do neceho slapne, tak se to pozna hned tady a ne buhvikde */
		if (name)mem_free(name);
		return;
	}
	else
#endif
	{
		chuligane = chuligane;
		document_id = document_id;
		image_id = image_id;
		if (name)mem_free(name);
		return;
	}
}


/* sets image alt to given value */
/* alt is deallocated after use with mem_free */
void js_upcall_set_image_alt(void* chuligane, long document_id, long image_id, unsigned char* alt)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_set_image_alt called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return;
		fd = jsint_find_document(document_id);
		if (!fd || !fd->f_data || !jsint_can_access(js_ctx, fd))return;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return;
		
		if (gi->alt)mem_free(gi->alt);
		gi->alt = stracpy(alt); /* radeji takhle, protoze to je bezpecnejsi: az PerM zase do neceho slapne, tak se to pozna hned tady a ne buhvikde */
		if (fd->f_data && gi->link_num >= 0 && gi->link_num < fd->f_data->nlinks)
		{
			struct link* l = &fd->f_data->links[gi->link_num];

			if (l->img_alt)mem_free(l->img_alt);
			l->img_alt = stracpy(alt);
		}
		if (alt)mem_free(alt);
		change_screen_status(fd->ses);
		print_screen_status(fd->ses);
		return;
	}
	else
#endif
	{
		chuligane = chuligane;
		document_id = document_id;
		image_id = image_id;
		if (alt)mem_free(alt);
		return;
	}
}


/* returns allocated string with source URL of given image or NULL on error */
unsigned char* js_upcall_get_image_src(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;
	
	if (F)
	{
		if (!js_ctx)internal("js_upcall_get_image_src called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return NULL;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return NULL;
		
		return stracpy(gi->orig_src);
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return NULL;
	}
}


/* changes image URL
 * gets struct fax_me_tender_string_2_longy
 *  num1 = document_id, num2 = image_id, string = url
 *
 * frees the string and the fax_me_tender struct with js_mem_free function
 */
void js_upcall_set_image_src(void* chuligane)
{
	unsigned char* zvrat;
	struct fax_me_tender_string_2_longy* fax = (struct fax_me_tender_string_2_longy*)chuligane;
	struct f_data_c* js_ctx;
#ifdef G
	struct f_data_c* fd;
	struct g_object_image* gi;
	long image_id, document_id;
	unsigned char* vecirek;
	if (F)
	{
		js_ctx = (struct f_data_c*)fax->ident;
		js_spec_vykill_timer(js_ctx->js->ctx, 0);
		if (!chuligane)internal("js_upcall_set_image_src called with NULL argument\n");
		if (!js_ctx)internal("js_upcall_set_image_src called with NULL context pointer\n");
		image_id = fax->obj_id;
		document_id = fax->doc_id;
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)goto abych_tu_nepovecerel;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))goto abych_tu_nepovecerel;

		gi = jsint_find_object(fd, image_id);

		if (!gi || !fd->f_data)goto abych_tu_nepovecerel;

		/* string joinnem s url */
		if (fd->f_data && fd->f_data->script_href_base) vecirek = join_urls(fd->f_data->script_href_base, fax->string);
		else if (fd->loc && fd->loc->url) vecirek = join_urls(fd->loc->url, fax->string);
		else vecirek = stracpy(fax->string);
		/* a mame to kompatidebilni s verzi pred jointem */

		change_image(gi, vecirek, fax->string, fd->f_data);
		if (vecirek) mem_free(vecirek);
		fd->f_data->uncacheable = 1;
	abych_tu_nepovecerel:
		;
	}
	else
#endif
	{
		js_ctx = (struct f_data_c*)fax->ident;
		if (!js_ctx)internal("js_upcall_set_image_src called with NULL context pointer\n");
		js_spec_vykill_timer(js_ctx->js->ctx, 0);
		if (!chuligane)internal("js_upcall_set_image_src called with NULL argument\n");
	}
	zvrat = stracpy(fax->string);
	js_mem_free(fax->string);
	js_mem_free(fax);
	js_downcall_vezmi_string(js_ctx->js->ctx, zvrat);
}


/* returns 1 if image has completed loading, 0 when not, -1 on error */
int js_upcall_image_complete(void* chuligane, long document_id, long image_id)
{
#ifdef G
	struct f_data_c* fd;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct g_object_image* gi;

	if (F)
	{
		if (!js_ctx)internal("js_upcall_image_complete called with NULL context pointer\n");
		if ((image_id & JS_OBJ_MASK) != JS_OBJ_T_IMAGE)return -1;
		fd = jsint_find_document(document_id);
		if (!fd || !jsint_can_access(js_ctx, fd))return -1;

		gi = jsint_find_object(fd, image_id);

		if (!gi)return -1;
		
		if (!gi->af || !gi->af->rq || !gi->af->rq->state)return -1;
		return gi->af->rq->state == O_OK;
	}
	else
#endif
	{
		document_id = document_id;
		image_id = image_id;
		return -1;
	}
}


/* returns parent of given frame (or document), or -1 on error or no permissions */
/* if frame_id is already top frame returns the same frame */
long js_upcall_get_parent(void* chuligane, long frame_id)
{
	struct f_data_c* fd, *ff;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;

	if (!js_ctx)internal("js_upcall_get_parent called with NULL context pointer\n");
	if ((frame_id & JS_OBJ_MASK) != JS_OBJ_T_FRAME && (frame_id & JS_OBJ_MASK) != JS_OBJ_T_DOCUMENT)return -1;
	fd = jsint_find_document(frame_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;

	for (ff = fd->parent; ff && !ff->rq; ff = ff->parent);
	
	if (!ff)ff = fd->ses->screen;
	return jsint_can_access(fd, ff) ? js_upcall_get_frame_id(ff) : -1;
}


/* returns top of given frame (or document), or -1 on error */
/* returns highest grandparent accessible from given frame */
long js_upcall_get_frame_top(void* chuligane, long frame_id)
{
	struct f_data_c* fd, *ff;
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;

	if (!js_ctx)internal("js_upcall_get_frame_top called with NULL context pointer\n");
	if ((frame_id & JS_OBJ_MASK) != JS_OBJ_T_FRAME && (frame_id & JS_OBJ_MASK) != JS_OBJ_T_DOCUMENT)return -1;
	fd = jsint_find_document(frame_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return -1;
	for (ff = fd->parent; ff; ff = ff->parent)
	{
		if (ff->rq)
		{
			if (!jsint_can_access(fd, ff))break;
			fd = ff;
		}
	}
	return js_upcall_get_frame_id(fd);
}


/* returns allocated field of subframes or NULL on error */
/* count cointains length of the field */
/* don't forget to free the field after use */
long* js_upcall_get_subframes(void* chuligane, long frame_id, int* count)
{
	struct f_data_c* js_ctx = (struct f_data_c*)chuligane;
	struct f_data_c* fd;
	struct f_data_c* f;
	int a;
	long* pole;
	*count = 0;

	if (!js_ctx)internal("js_upcall_get_subframes called with NULL context pointer\n");
	if ((frame_id & JS_OBJ_MASK) != JS_OBJ_T_FRAME && (frame_id & JS_OBJ_MASK) != JS_OBJ_T_DOCUMENT)return NULL;
	fd = jsint_find_document(frame_id);
	if (!fd || !jsint_can_access(js_ctx, fd))return NULL;

	foreach(f, fd->subframes)
	if (jsint_can_access(fd, f))(*count)++;

	if (!*count)return NULL;
	if ((unsigned)*count > MAXINT / sizeof(long)) overalloc();
	pole = mem_alloc((*count) * sizeof(long));
	
	a = 0;
	foreach(f, fd->subframes)
	if (jsint_can_access(fd, f))
	{
		pole[a] = js_upcall_get_frame_id(f);
		a++;
	}
	return pole;
}


/*---------------------             DOWNCALLS          ---------------------------*/

void js_downcall_game_over(void* context)
{
	struct f_data_c* fd = (struct f_data_c*)(((js_context*)(context))->ptr);
	
	/* js_error(_(TEXT(T_SCRIPT_KILLED_BY_USER),fd->ses->term),context);
	 * Tato hlaska me srala. Na to bych neprisel, ze jsem prave zabil
	 * rucne javascript. */
	if (fd->ses->default_status)mem_free(fd->ses->default_status), fd->ses->default_status = NULL; /* pekne uklidime bordylek, ktery nam BFU nacintalo do status lajny */
	jsint_destroy(fd);
#if 0
	js_durchfall = 0;
	if (((js_context*)context)->running)
		js_volej_kolbena(context);
	/* Kolben - ale nespi mi - co s tim budeme delat? */
	((js_context*)context)->running = 0;
#endif
	
}



void js_downcall_vezmi_int(void* context, int i)
{
}


void js_downcall_vezmi_float(void* context, double f)
{
}

#else

void jsint_execute_code(struct f_data_c* fd, unsigned char* code, int len, int write_pos, int onclick_submit, int onsubmit, struct event* ev)
{
}

void jsint_destroy(struct f_data_c* fd)
{
}

void jsint_scan_script_tags(struct f_data_c* fd)
{
}

void jsint_destroy_document_description(struct f_data* f)
{
}

int jsint_get_source(struct f_data_c* fd, unsigned char** start, unsigned char** end)
{
	return 0;
}

#endif
